React Hook Form with shadcn/ui
Build accessible, type-safe shadcn/ui forms with React Hook Form and Zod.
The shadcn/ui Form component composes React Hook Form's FormProvider and Controller. It supplies accessible labels, descriptions, and error messages around shadcn/ui controls.
Installation
Add the shadcn/ui form primitives, then install the resolver and schema library used by this example:
npx shadcn@latest add form input buttonnpm install @hookform/resolvers zod
Complete example
"use client"import { zodResolver } from "@hookform/resolvers/zod"import { useForm } from "react-hook-form"import { z } from "zod"import { Button } from "@/components/ui/button"import {Form,FormControl,FormField,FormItem,FormLabel,FormMessage,} from "@/components/ui/form"import { Input } from "@/components/ui/input"const schema = z.object({username: z.string().min(2, "Enter at least two characters"),})export function UsernameForm() {const form = useForm<z.infer<typeof schema>>({resolver: zodResolver(schema),defaultValues: { username: "" },})return (<Form {...form}><form onSubmit={form.handleSubmit(console.log)}><FormFieldcontrol={form.control}name="username"render={({ field }) => (<FormItem><FormLabel>Username</FormLabel><FormControl><Input {...field} /></FormControl><FormMessage /></FormItem>)}/><Button type="submit">Save</Button></form></Form>)}
Two different Form components
The shadcn/ui Form above provides React Hook Form context. It is different from React Hook Form's submission-oriented Form component, which can send values to an endpoint or Server Action.
Native and controlled inputs
- Use
registerdirectly when a component exposes a native input ref and standard change events. - Use shadcn/ui
FormFieldor React Hook Form'sControllerfor controlled components such as custom selects, date pickers, and switches. - Define every controlled field in
defaultValues; avoidundefinedas a controlled value. - Spread
fieldonto the actual control sovalue,onChange,onBlur,name, andrefstay connected.
See the dedicated Zod guide, Controller, and FormProvider.