Components
Form
react-hook-form + zod. Wraps Controller, wires labels to inputs, and surfaces validation messages using semantic destructive tokens.
When to usefor any form with more than one field where validation, focus management, or async submission matters.
Import
import {
Form, FormField, FormItem, FormLabel,
FormControl, FormDescription, FormMessage,
} from "@ravnhq/efni/form"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"New mission
const schema = z.object({
name: z.string().min(3),
operator: z.string().min(1),
scheduled: z.string().min(1),
})
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: { name: "", operator: "", scheduled: "" },
})
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Mission name</FormLabel>
<FormControl><Input {...field} /></FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Create</Button>
</form>
</Form>Accessibility
| Key | Action |
|---|---|
| Tab | Move through fields in DOM order. |
| Enter | Submit the form when focused inside a text input. |
FormControlclones its child to injectid,aria-describedby, andaria-invalid— you don't manage them yourself.- FormMessage renders nothing when there's no error, so the field height doesn't jump.
- Every input should have a FormLabel — never skip it in favour of a placeholder.
Guidelines
Do
- Keep the field label above the input; helper text below in FormDescription.
- Validate on submit for short forms; on blur for longer ones.
- Reset the form after a successful submit; don't clear it silently.
Don't
- Don't nest a FormField in a FormField — one Controller per field.
- Don't use placeholder text as the label — screen readers skip it and translation breaks.
- Don't gate submit behind form.formState.isValid alone — server errors won't be represented.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
Form | FormProviderProps | — | Re-export of react-hook-form FormProvider. |
FormField | ControllerProps | — | Wraps <Controller>; must be inside <Form>. |
FormItem | HTMLAttributes<HTMLDivElement> | — | Container that provides the id context for label + control. |
FormLabel | React.ComponentProps<typeof Label> | — | Label wired to the item's id; goes red on error. |
FormControl | { children: ReactElement } | — | Clones its child to inject id + aria-* attributes. |
FormDescription | HTMLAttributes<HTMLParagraphElement> | — | Helper text, wired via aria-describedby. |
FormMessage | HTMLAttributes<HTMLParagraphElement> | — | Shows the field's error message, or nothing when valid. |