Form

A set of composable layout primitives for building forms: handling spacing, labeling, error display, and section grouping. Optionally pair with the useZodForm hook for Zod schema validation powered by react-hook-form.

Installation

npx dafink-ui add form

Also installs: Input, Button

Requires: zod, react-hook-form, @hookform/resolvers

Demos

We will never share your email.

Manual state, no validation

useZodForm is optional; the layout primitives work with plain useState just as well, for a one-off field that doesn't need a schema.

Mixed field types

Select and Switch already render their own label; wrap them in FormField for spacing and FormSection for grouping. For the optional Textarea, FormLabel adds a muted "(optional)" suffix instead of a hint below the field.

Notification preferences

Controls how often we email you about account activity.

Disabled fields

FormDescription doubles as an explanation for why a field is locked; pair it with disabled instead of just hiding the reason.

Account

Contact sales to change your plan.

Composition

<Form>
<FormSection>
<FormField>
<FormLabel />
<FormControl />
<FormDescription />
<FormMessage />
</FormField>
</FormSection>
</Form>

Props

NameTypeDefaultDescription
classNamestring""Additional classes merged onto the form element.
schemaz.ZodType-useZodForm(schema, options): a Zod schema describing the form shape. Passed to zodResolver internally so validation runs on submit (and on change/blur if configured via options.mode).
optionsOmit<UseFormProps<z.infer<TSchema>>, 'resolver'>undefineduseZodForm(schema, options): any react-hook-form useForm option (defaultValues, mode, etc.) except resolver, which useZodForm sets for you.

Accessibility

Form is a layout primitive, not an interactive widget: it has no keyboard handling of its own. Focus order and activation come entirely from the native elements it composes — a plain form, a fieldset/legend pairing in FormSection, and a label with htmlFor in FormLabel. The only ARIA usage is FormMessage's role="alert", which announces a validation message to screen readers as soon as it renders (it renders nothing when there's no error). Consumers are responsible for pairing each FormLabel's htmlFor with its control's id, adding aria-required/required to required inputs (the visual asterisk is aria-hidden), and linking FormDescription via aria-describedby so hint text is announced too.