Form
Building forms with React Hook Form and Zod.
Powered by
Installation
npx shadcn@latest add @redpanda/formpnpm dlx shadcn@latest add @redpanda/formyarn dlx shadcn@latest add @redpanda/formbunx shadcn@latest add @redpanda/formnubx shadcn@latest add @redpanda/formaube dlx shadcn@latest add @redpanda/formWhen to use
Use this decision tree to determine when to use the Form components:
Usage
Create a form schema
Define the shape of your form using a Zod schema. You can read more about using Zod in the Zod documentation.
'use client';
import { z } from 'zod';
const formSchema = z.object({ username: z.string().min(2).max(50),});Define a form
Use the useForm hook from react-hook-form to create a form.
'use client';
import { zodResolver } from '@hookform/resolvers/zod';import { useForm } from 'react-hook-form';import { z } from 'zod';
const formSchema = z.object({ username: z.string().min(2, { message: 'Username must be at least 2 characters.', }),});
export function ProfileForm() { // 1. Define your form. const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: { username: '', }, });
// 2. Define a submit handler. function onSubmit(values: z.infer<typeof formSchema>) { // Do something with the form values. // ✅ This will be type-safe and validated. console.log(values); }}Since FormField is using a controlled component, you need to provide a default value for the field. See the React Hook Form docs to learn more about controlled components.
Build your form
We can now use the <Form /> components to build our form.
'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, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';import { Input } from '@/components/ui/input';
const formSchema = z.object({ username: z.string().min(2, { message: 'Username must be at least 2 characters.', }),});
export function ProfileForm() { // ...
return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> <FormField control={form.control} name="username" render={({ field }) => ( <FormItem> <FormLabel>Username</FormLabel> <FormControl> <Input placeholder="shadcn" {...field} /> </FormControl> <FormDescription>This is your public display name.</FormDescription> <FormMessage /> </FormItem> )} /> <Button type="submit">Submit</Button> </form> </Form> );}Done
That’s it. You now have a fully accessible form that is type-safe with client-side validation.
Examples
Simple Form
Use SimpleFormField for a single control such as Input or Textarea. For compound cards, option groups and date
popovers, compose FormField, FormItem and FormControl so the field’s label, ref and validation attributes reach
the actual interactive control. Put FormControl around PopoverTrigger, not inside its render element: this lets
the field supply the trigger ID that its label targets.
Both profile examples share one schema and concrete profile email, notification, date-of-birth and email-preference
components. These live together in the profile example library; they are not generic field templates. Username and
Bio deliberately demonstrate manual FormField composition in the full example and SimpleFormField in the simple
example. Each option retains its own associated label, actual control ref and validation description.
The Bio field accepts 10–160 characters, and failed submission focuses the first invalid control. Use existing
FormItem layout and spacing recipes instead of local spacing overrides. Horizontal rows use the standard spacing
scale; layout="card-horizontal" owns switch-card alignment and padding.
Required fields
JSON Field
See the following links for more examples on how to use the <Form /> component with other components:
- Checkbox
- Date Picker
- Input
- JSON Field
- Radio Group
- Select
- Multi Select
- Switch
- Textarea
- Combobox
- Field choice cards
Credits
- We use React Hook Form for form state and Base UI’s useRender for control composition.
- We take our inspiration from Shadcn UI for the form style.