mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-23 04:35:46 +00:00
* feat(v4): update home page * fix * fix: cards * feat(v4): charts page * feat: update pages * feat: colors * fix * feat: add docs * feat: mdx work * fix * fix * fix: sidebar * fix: lint * feat: updates * feat: update components * feat: fix docs * fix: responsive * feat: implement cmdk * fix: update navigation menu demo * fix: code style * fix: themes * feat: implement blocks page * fix: docs config * refactor * fix: outputFileTracingIncludes * fix * fix: output * fix * fix: registry * refactor: move docs * debug: docs * debug * revert * fix: mjs * deps: pin fumadocs * debug * fix: downgrade next * fix: index page * refactor: move mdx components * fix: remove copy button * fix * was it zod * yes it was * remove copy page * fix: color page * fix: colors page * fix: meta colors * fix: copy button * feat: sync registry * fix: registry build script * feat: update port * feat: clean up examples * fix * fix: mobile nav * fix: blur for mobile * fix: sidebar nav * feat: update examples * fix: build scripts * feat: update components * feat: restyle * fix: types * fix: styles * fix: margins * fix: screenshots * fix * feat: update theme * fix: charts nav * fix: image * feat: optimize images * fix: menu * fix: card * fix: border * check * feat: implement charts page * fix: charts * fix: og images * feat: extend touch * fix: static * fix: sizing * fix: mobile screenshots * fix: page nav * fix * feat: update favicon * fix: theme selector * fix: feedback * fix: sink * docs: update * fix: styles * chore: update registry * fix: command * fix * fix: minor updates * fix: typography on smaller devices * fix: format * fix: remove unused icon * feat: update favicon * fix: typography * docs: typography page * fix: steps
256 lines
6.2 KiB
Plaintext
256 lines
6.2 KiB
Plaintext
---
|
|
title: React Hook Form
|
|
description: Building forms with React Hook Form and Zod.
|
|
links:
|
|
doc: https://react-hook-form.com
|
|
---
|
|
|
|
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 `<FormField>` component to compose accessible forms using Radix UI components.
|
|
|
|
## Features
|
|
|
|
The `<Form />` component is a wrapper around the `react-hook-form` library. It provides a few things:
|
|
|
|
- Composable components for building forms.
|
|
- A `<FormField />` 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
|
|
<Form>
|
|
<FormField
|
|
control={...}
|
|
name="..."
|
|
render={() => (
|
|
<FormItem>
|
|
<FormLabel />
|
|
<FormControl>
|
|
{ /* Your form field */}
|
|
</FormControl>
|
|
<FormDescription />
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</Form>
|
|
```
|
|
|
|
## Example
|
|
|
|
```tsx showLineNumbers
|
|
const form = useForm()
|
|
|
|
<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>
|
|
)}
|
|
/>
|
|
```
|
|
|
|
## Installation
|
|
|
|
<CodeTabs>
|
|
|
|
<TabsList>
|
|
<TabsTrigger value="cli">CLI</TabsTrigger>
|
|
<TabsTrigger value="manual">Manual</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="cli">
|
|
|
|
<Steps>
|
|
|
|
### Command
|
|
|
|
```bash
|
|
npx shadcn@latest add form
|
|
```
|
|
|
|
</Steps>
|
|
|
|
</TabsContent>
|
|
|
|
<TabsContent value="manual">
|
|
|
|
<Steps>
|
|
|
|
<Step>Install the following dependencies:</Step>
|
|
|
|
```bash
|
|
npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod
|
|
```
|
|
|
|
<Step>Copy and paste the following code into your project.</Step>
|
|
|
|
<ComponentSource name="form" title="components/ui/form.tsx" />
|
|
|
|
<Step>Update the import paths to match your project setup.</Step>
|
|
|
|
</Steps>
|
|
|
|
</TabsContent>
|
|
|
|
</CodeTabs>
|
|
|
|
## Usage
|
|
|
|
<Steps>
|
|
|
|
### 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<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](https://react-hook-form.com/docs/usecontroller) to learn more about controlled components.
|
|
|
|
### Build your form
|
|
|
|
We can now use the `<Form />` 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 (
|
|
<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.
|
|
|
|
<ComponentPreview
|
|
name="input-form"
|
|
className="[&_[role=tablist]]:hidden [&>div>div:first-child]:hidden"
|
|
/>
|
|
|
|
</Steps>
|
|
|
|
## Examples
|
|
|
|
See the following links for more examples on how to use the `<Form />` component with other components:
|
|
|
|
- [Checkbox](/docs/components/checkbox#form)
|
|
- [Date Picker](/docs/components/date-picker#form)
|
|
- [Input](/docs/components/input#form)
|
|
- [Radio Group](/docs/components/radio-group#form)
|
|
- [Select](/docs/components/select#form)
|
|
- [Switch](/docs/components/switch#form)
|
|
- [Textarea](/docs/components/textarea#form)
|
|
- [Combobox](/docs/components/combobox#form)
|