--- description: Writing Forms with Shadcn UI, Server Actions, Zod globs: apps/**/*.tsx,packages/**/*.tsx alwaysApply: false --- # Forms - Use React Hook Form for form validation and submission. - Use Zod for form validation. - Use the `zodResolver` function to resolve the Zod schema to the form. - Use Server Actions [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc) for server-side code handling - Use Sonner for writing toasters for UI feedback - Never add generics to `useForm`, use Zod resolver to infer types instead Follow the example below to create all forms: ## Define the schema Zod schemas should be defined in the `schema` folder and exported, so we can reuse them across a Server Action and the client-side form: ```tsx // _lib/schema/create-note.schema.ts import { z } from 'zod'; export const CreateNoteSchema = z.object({ title: z.string().min(1), content: z.string().min(1), }); ``` ## Create the Server Action Server Actions [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc) can help us create endpoints for our forms. ```tsx 'use server'; import { z } from 'zod'; import { enhanceAction } from '@kit/next/actions'; import { CreateNoteSchema } from '../schema/create-note.schema'; export const createNoteAction = enhanceAction( async function (data, user) { // 1. "data" has been validated against the Zod schema, and it's safe to use // 2. "user" is the authenticated user // ... your code here return { success: true, }; }, { auth: true, schema: CreateNoteSchema, }, ); ``` ## Create the Form Component Then create a client component to handle the form submission: ```tsx 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { Textarea } from '@kit/ui/textarea'; import { Input } from '@kit/ui/input'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@kit/ui/form'; import { toast } from '@kit/ui/sonner'; import { useTranslation } from 'react-i18next'; import { CreateNoteSchema } from '../_lib/schema/create-note.schema'; export function CreateNoteForm() { const [pending, startTransition] = useTransition(); const { t } = useTranslation(); const form = useForm({ resolver: zodResolver(CreateNoteSchema), defaultValues: { title: '', content: '', }, }); const onSubmit = (data) => { startTransition(async () => { await toast.promise(createNoteAction(data), { loading: t('notes:creatingNote`), success: t('notes:createNoteSuccess`), error: t('notes:createNoteError`) }) }); }; return (
); } ``` Always use `@kit/ui` for writing the UI of the form.