Back to Design System

Form

Higher-level helpers on top of React Hook Form. Provides accessible wiring between inputs, labels, descriptions, and validation messages.

Overview

  • Form wraps `useForm` with context so nested fields can access `control`.
  • FormField uses `Controller` to bind inputs to field names.
  • FormControl augments inputs with `id`, `aria-describedby`, and `aria-invalid` automatically.
  • FormMessage renders validation errors or custom fallback copy.

Example form

Combine with Pixexid inputs and buttons to deliver consistent validation feedback.

Shown in dashboards and share sheets.

import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"

import {
  Form,
  FormField,
  FormItem,
  FormLabel,
  FormControl,
  FormDescription,
  FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

const schema = z.object({
  projectName: z.string().min(2, "Project name must be at least 2 characters"),
  email: z.string().email("Enter a valid email"),
})

type FormValues = z.infer<typeof schema>

export function CreateProjectForm() {
  const form = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: {
      projectName: "",
      email: "",
    },
  })

  return (
    <Form {...form}>
      <form className="space-y-6" onSubmit={form.handleSubmit(console.log)}>
        <FormField
          control={form.control}
          name="projectName"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Project name</FormLabel>
              <FormControl>
                <Input placeholder="Pixexid Web" {...field} />
              </FormControl>
              <FormDescription>Shown in the project list.</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Notification email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="you@pixexid.com" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <Button type="submit">Create project</Button>
      </form>
    </Form>
  )
}

Key exports

NameTypeDescription
FormFormProvider from react-hook-formWraps your form with context so `FormField` + hooks can access state.
FormFieldControllerProps<TFieldValues, TName>Binds a form control to a field name using React Hook Form Controller.
FormItemReact.HTMLAttributes<HTMLDivElement>Layout wrapper that generates unique IDs for label/description/message hooks.
useFormField() => { name, id, error, ... }Hook used internally by `FormLabel`, `FormControl`, `FormMessage`, and `FormDescription`.

Implementation tips

Best practices

  • Call `form.handleSubmit` in `<form onSubmit>` to ensure validation runs.
  • Pass `rules` or Zod resolvers to enforce constraints.
  • Combine `FormDescription` + `FormMessage` for assistive descriptions.

Accessibility

  • `FormLabel` attaches `htmlFor` automatically to the control.
  • `FormMessage` uses `aria-describedby` to announce errors.
  • Ensure `defaultValues` include every field to avoid uncontrolled warnings.