💠 Form
Allows users to input information.
Usage
The Form component is a container for various form elements like inputs, checkboxes, and buttons.
It's used to collect user information and facilitate user interactions that require data entry and submission.
Forms are the backbone of tasks like registration, search, and feedback, making them an integral part of user interfaces.
When to use
- Data collection: Use Forms when you need to collect information from users, such as during sign-ups, checkouts, or profile updates.
- User interactions: Employ Forms to facilitate interactions where user input is required, such as search bars or contact pages.
- Task completion: Utilize Forms to guide users through multi-step tasks, using appropriate controls and validation to ensure the collection of necessary data.
- Feedback gathering: Forms are essential for gathering user feedback, whether through surveys, contact forms, or review submissions.
Avoid overly complex forms that can overwhelm users; break them into manageable sections if needed. Do not ask for unnecessary information—keep form fields to a minimum to reduce user fatigue. Ensure that forms are not a barrier to user tasks; they should be helpful tools that facilitate user actions, not hinder them.
React Hook Form
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 (opens in a new tab) and zod (opens in a new tab). 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.
Anatomy
<Form>
<FormField
control={...}
name="..."
render={() => (
<FormItem>
<FormLabel />
<FormControl>
{ /* Your form field */}
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
)}
/>
</Form>
Example
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>
)}
/>
How to use
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 (opens in a new tab).
"use client";
import * as 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 * as 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 (opens in a new tab) 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 * as z from "zod";
import { Button } from "@fusillo/ui/components/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@fusillo/ui/components/form";
import { Input } from "@fusillo/ui/components/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
See the following links for more examples on how to use the <Form />
component with other components:
Useful links
Here you go! Here are all the resources you might need if you are a designer, a developer or a content manager: