Installation
npx logic2b@latest add formAnatomy
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
const formSchema = z.object({
username: z.string().min(2),
})
function ProfileForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { username: "" },
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)}>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<input {...field} />
</FormControl>
<FormDescription>Your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
)
}
FormField wraps react-hook-form’s Controller and provides field context to
FormLabel, FormControl, FormDescription and FormMessage — they read
validation state (error, id) from that context, so labels and messages stay
wired to the right input automatically.
API
Form is an alias for react-hook-form’s FormProvider, so spread your useForm
return value onto it. The pieces below consume field context.
| Prop | Type | Default |
|---|---|---|
control (FormField) |
Control — from useForm() |
— |
name (FormField) |
string — the field path |
— |
render (FormField) |
({ field, fieldState, formState }) => ReactElement |
— |
...form (Form) |
the object returned by useForm() |
— |
useFormField() |
hook returning { id, name, error, formItemId, formDescriptionId, formMessageId } |
— |
Examples
Sign In
Validate an email and password with zod.
Textarea
Accessibility
FormControlsetsid,aria-describedbyandaria-invalidfrom field context, so the label, description and error message are all wired to the input without manualhtmlFor/idbookkeeping.- On validation failure
FormMessagerenders the error text with the id referenced byaria-describedby, andFormLabelturns destructive — the invalid state is conveyed both visually and to screen readers. - Because each control gets a unique generated id, labels always point at the right input even when the field is reused in a list.