Khi nào dùng
- •Form có input/validation client-side (login, admin create/edit).
- •Không dùng cho Server Components; form component sẽ là
'use client'.
Nguồn trong repo
- •Shadcn form wrapper (không sửa):
apps/web/src/components/ui/form.tsx - •Form thật (admin/blog):
apps/web/src/components/admin/blog/post-form.tsx
Pattern chuẩn
- •Khai báo schema Zod (ở
apps/web/src/schemas/*) và type:
- •
export type XxxFormData = z.infer<typeof xxxSchema>
- •Trong Client Component:
- •
const form = useForm<XxxFormData, unknown, XxxFormData>({ resolver: zodResolver(xxxSchema), defaultValues })
- •Render với Shadcn Form components:
- •
<Form {...form}>+<FormField name="..." render={({ field }) => (...) } /> - •Dùng
<FormMessage />để hiển thị lỗi.
i18n/UI
- •Text UI phải tiếng Việt và ưu tiên lấy từ
next-intl:- •
const t = useTranslations('admin.blog')
- •
- •Tránh hardcode English strings cho label/button/toast.
Mapping dữ liệu với DB
- •Dữ liệu đọc/ghi DB nên theo type
Database['public']['Tables'][...]['Row'|'Insert'|'Update']. - •Khi form có field nullable/optional, đồng bộ với Zod (
optional().nullable()) và defaultValues (null/'' đúng ngữ nghĩa).
Common gotchas
- •
defaultValuesphải stable (đặc biệt khi edit). Nếu phụ thuộcpost, set lại bằngform.reset(...)hoặc đảm bảo props ổn định. - •Với boolean nullable từ DB, normalize về boolean trong form (ví dụ repo dùng
allow_comments !== false). - •Với UUID: dùng
z.string().uuid()để match DB.