Form
React Hook Form scaffolding that wires labels, controls, descriptions, and validation messages.
@garn/ui/formDefault
A single RHF-bound, labeled field with description + submit.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add form- Registry
form- Source
- form.tsx
- Deps
react-hook-form@radix-ui/react-slot- Registry deps
utilslabel
Import
import { Form } from "@garn/ui/form";Props
Generated from the component's TypeScript types.
No component-specific props — this is a thin wrapper over its native element.
Styling
Target these data-slots and remap these tokens to restyle without forking the component.
[data-slot="form-item"]| Property | Token | Tier |
|---|---|---|
fieldGap | --garn-gap-field | semantic · air |
Overview
Form is the accessible wiring layer over React Hook Form: it ties each field's label, control, description, and error message together so the ARIA plumbing is correct by default. Form is the RHF provider, FormField binds a name via Controller, and within a FormItem the FormLabel / FormControl / FormDescription / FormMessage parts share one id set — FormControl injects id + aria-describedby + aria-invalid onto your control. Use it for any real form; a single uncontrolled field doesn't need the machinery.
Guidelines
Wrap each field in FormField + FormItem. That's what generates the shared id and links label↔control↔description↔error; reaching past it to hand-wire htmlFor/aria-* reintroduces the bugs Form exists to prevent. Put your control inside FormControl so the a11y attributes land on it.
Validate with a schema and surface errors inline. Drive RHF with a resolver (Zod/Yup) so FormMessage renders the real validation message beside the field and aria-invalid styles it; show errors on submit/blur, not on every keystroke. Keep one clear message per field.
Write helpful labels and descriptions. Use FormDescription for format hints and requirements rather than cramming them into the label or a placeholder, mark optional fields explicitly, and keep the submit action's state honest (disable/spinner while pending).
Best practices
- Wrap fields in FormField (control + name) and render the control inside FormControl.
- Put one FormItem per field so the label/control/message ids stay linked.
- Let FormMessage render the error — it pulls from RHF field state automatically.
- Don't hand-wire aria-describedby/aria-invalid — FormControl does it.
- Don't call useFormField outside a FormField — it throws.
Content guidelines
- Write field labels as short noun phrases; reserve helper text for one line of guidance.
- Make error messages specific and fixable (“Enter a valid email”), tied to the field.
Accessibility
- Role
form
- • FormControl auto-wires id + aria-describedby (description + message) + aria-invalid onto the control.
- • FormLabel's htmlFor targets the control; FormMessage announces the validation error.
- Status
- Stable
- Version
v0.3.0- Introduced
v0.1.0
garn doesn't yet record a per-release version history. This reflects the component's current lifecycle state; full changelog entries will appear once the metadata pipeline emits a lifecycle.history field.