Form
Build forms with React Hook Form and Zod, enabling efficient form handling and schema validation. This combination ensures a robust, user-friendly data input experience, providing reliable validation and streamlined management of form state for more seamless interactions.
Introduction
Forms come with unique behaviors, validation requirements, and data handling needs. Combining these variations into a single component offers limited modular benefits. Rather than providing a universal form component, a tutorial is offered on building a custom solution. This tutorial leverages React Hook Form and Zod to demonstrate how to incorporate complex features, ensuring the form meets specific requirements.
- 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. - You have full control over the markup and styling.
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
Next, 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)
}
}
Note: 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="Michigan Catastrophic Claims Association" {...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.
Additional Components
While this guide focuses on the <Form />
component, the following links provide examples on how to expand the <Form />
component with field components: