React Hook Form

Building forms with React Hook Form and Zod. Please refer to the Docs 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 and zod. 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.

Installation

The Form component is part of our UI library. You can import it directly from the components directory.


Usage

Here is how to use the Form component in your project.

React TSX
1import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from "@/components/modern-ui/form"
2import { useForm } from "react-hook-form"
3import { zodResolver } from "@hookform/resolvers/zod"
4import * as z from "zod"

React TSX
1const formSchema = z.object({
2  username: z.string().min(2).max(50),
3})
4
5export function MyForm() {
6  const form = useForm({
7    resolver: zodResolver(formSchema),
8    defaultValues: {
9      username: "",
10    },
11  })
12
13  function onSubmit(values) {
14    console.log(values)
15  }
16
17  return (
18    <Form {...form}>
19      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
20        <FormField
21          control={form.control}
22          name="username"
23          render={({ field }) => (
24            <FormItem>
25              <FormLabel>Username</FormLabel>
26              <FormControl>
27                <Input placeholder="johndoe" {...field} />
28              </FormControl>
29              <FormDescription>
30                Your public display name.
31              </FormDescription>
32              <FormMessage />
33            </FormItem>
34          )}
35        />
36        <Button type="submit">Submit</Button>
37      </form>
38    </Form>
39  )
40}

Examples

Default Form

Form Validation

Form With Multiple Fields

This is your public display name.

You can @mention other users.

Receive emails about your account activity.