Redpanda UI
RC
Redpanda UI

Form

Building forms with React Hook Form and Zod.

Made by shadcn
Loading component...

When to use

Use this decision tree to determine when to use the Form components:

Installation

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.

components/example-form.tsx
"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.

components/example-form.tsx
"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 to learn more about controlled components.

Build your form

We can now use the <Form /> components to build our form.

components/example-form.tsx
"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.

Loading component...

Examples

Simple Form

A comprehensive form example showcasing the SimpleFormField component for cleaner, more maintainable form code.

Loading component...

Required fields

Loading component...

See the following links for more examples on how to use the <Form /> component with other components:

Credits

  • We use Radix UI for the form component.
  • We take our inspiration from Shadcn UI for the form style.

Built by malinskibeniamin. The source code is available on GitHub.

On this page