--- title: Form description: Building forms with React Hook Form and Zod. links: doc: https://react-hook-form.com --- import { InfoIcon } from "lucide-react" } title="We are not actively developing this component anymore."> The Form component is an abstraction over the `react-hook-form` library. Going forward, we recommend using the [``](/docs/components/field) component to build forms. See the [Form](/docs/forms) documentation for more information. Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex. Well-designed HTML forms are: - Well-structured and semantically correct. - Easy to use and navigate (keyboard). - Accessible with ARIA attributes and proper labels. - Has support for client and server side validation. - Well-styled and consistent with the rest of the application. In this guide, we will take a look at building forms with [`react-hook-form`](https://react-hook-form.com/) and [`zod`](https://zod.dev). We're going to use a `` component to compose accessible forms using Radix UI components. ## Features The `
` component is a wrapper around the `react-hook-form` library. It provides a few things: - Composable components for building forms. - A `` component for building controlled form fields. - Form validation using `zod`. - Handles accessibility and error messages. - Uses `React.useId()` for generating unique IDs. - Applies the correct `aria` attributes to form fields based on states. - Built to work with all Radix UI components. - Bring your own schema library. We use `zod` but you can use anything you want. - **You have full control over the markup and styling.** ## Anatomy ```tsx showLineNumbers ( { /* Your form field */} )} /> ``` ## Example ```tsx showLineNumbers const form = useForm() ( Username This is your public display name. )} /> ``` ## Installation CLI Manual ### Command ```bash npx shadcn@latest add form ``` Install the following dependencies: ```bash npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod ``` Copy and paste the following code into your project. Update the import paths to match your project setup. ## 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](https://zod.dev). ```tsx showLineNumbers title="components/example-form.tsx" {3,5-7} "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. ```tsx showLineNumbers title="components/example-form.tsx" {3-4,14-20,22-27} "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>({ resolver: zodResolver(formSchema), defaultValues: { username: "", }, }) // 2. Define a submit handler. function onSubmit(values: z.infer) { // 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](https://react-hook-form.com/docs/usecontroller) to learn more about controlled components. ### Build your form We can now use the `
` components to build our form. ```tsx showLineNumbers title="components/example-form.tsx" {7-17,28-50} "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 ( ( Username This is your public display name. )} /> ) } ``` ### Done That's it. You now have a fully accessible form that is type-safe with client-side validation.