Component guideEditable source

Form

React Hook Form fields with linked labels, descriptions, and validation messages.

Original component docs
On this page5 sectionsBack to top

Use these helpers with React Hook Form to connect fields to validation and accessible descriptions. Form is a provider, not a native form element. Render a form and attach its submit handler.

Live preview
Loading preview...

#Installation

Complete the base setup. For manual installation:

npx shadcn@latest add https://neobrutal-ui.andongmin.com/r/form.json

The complete example additionally uses Input and Button. Install them before copying the Code or Usage example. This minimal example uses built-in field rules; the preview demonstrates schema validation.

npx shadcn@latest add https://neobrutal-ui.andongmin.com/r/input.json https://neobrutal-ui.andongmin.com/r/button.json

#Usage

"use client";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  Form,
  FormField,
  FormItem,
  FormLabel,
  FormControl,
  FormDescription,
  FormMessage,
} from "@/components/ui/form";

type Values = { username: string };

export default function ProfileForm() {
  const form = useForm<Values>({ defaultValues: { username: "" } });
  const [message, setMessage] = useState("");
  return (
    <Form {...form}>
      <form
        className="grid max-w-sm gap-4"
        onSubmit={form.handleSubmit((values) => setMessage(`Local preview: ${values.username}`))}
      >
        <FormField
          control={form.control}
          name="username"
          rules={{ required: "Enter a display name." }}
          render={({ field }) => (
            <FormItem>
              <FormLabel>Display name</FormLabel>
              <FormControl>
                <Input autoComplete="nickname" {...field} />
              </FormControl>
              <FormDescription>Visible to workspace members.</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Save preview</Button>
        <p role="status">{message}</p>
      </form>
    </Form>
  );
}

#Optional help and custom controls

FormDescription and FormMessage are optional. Render at most one of each per FormItem. Their references are registered when mounted and removed when hidden or unmounted, including when an error clears. Existing aria-describedby references on the control and on FormControl are combined without duplicates. Application-owned reference targets must exist in the document.

An explicit control ID is preserved and its label follows it after hydration. For a label that must also target a custom control before hydration, pass the same explicit ID as FormLabel.htmlFor. Custom controls must forward their HTML props and ref to the actual focusable element. The wrapper composes child and wrapper event handlers and refs using Base UI, not a second input.

Loading preview...

#API reference

PartPurpose and usage
FormProvides React Hook Form methods. Spread the result of useForm onto it.
FormFieldBinds a typed field using control, name, and render.
FormItemWraps one field and shares its label, control, and mounted message IDs.
FormLabelLabels the control. Place it inside FormItem.
FormControlPasses the field ID, mounted help references, and invalid state to one control that forwards props and ref.
FormDescriptionOptional help text linked to the control after mounting; supports a custom ID.
FormMessageOptional validation feedback linked while rendered; supports a custom ID.

Form helpers require Form, FormField, and FormItem in the field tree. A missing provider produces an explicit diagnostic. FormControl does not accept an empty child, text, a Fragment, or multiple children. Error text uses the shared --error token, independently of filled destructive-action colors.

#Accessibility

Keep labels visible and errors associated with the field. FormControl must wrap the actual control, not an unrelated layout container. Use proper input types and autocomplete attributes. Validate on the server as well as the client. During asynchronous submission show pending state, prevent accidental repeats, and keep error feedback available.