Form
Accessible field structure for labels, descriptions, and validation around existing controls.
Overview
Form is a lightweight layout and accessibility layer for Vinyaas inputs — not a form-state framework. Keep values, schemas, and submit handling in your own React state or server actions.
Compose fields in this order: Form → FormField → FormItem → FormLabel, FormControl, optional FormDescription, and optional FormMessage.
FormFieldowns unique ids plus invalid and disabled state.FormControlmerges those associations onto a single child control.- For
SelectandCombobox, wrap the trigger — not the root provider. - Pass
errorintoFormFieldto light up labels, borders, and alert messages with destructive tokens.
Preview
Update the name and email on your public account.
Installation
Add the component with the CLI. It copies the files in and installs anything they depend on.
npx vinyaas add formUsage
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
export function ProfileForm() {
return (
<Card className="border-border w-full max-w-md">
<CardHeader>
<CardTitle>Profile</CardTitle>
<CardDescription>
Update the name and email on your public account.
</CardDescription>
</CardHeader>
<CardContent>
<Form
onSubmit={(event) => {
event.preventDefault();
}}
>
<FormField name="name">
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Ada Lovelace" autoComplete="name" />
</FormControl>
</FormItem>
</FormField>
<FormField name="email">
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="ada@example.com"
autoComplete="email"
/>
</FormControl>
<FormDescription>
We will use this email for account notifications.
</FormDescription>
</FormItem>
</FormField>
<div className="flex justify-end pt-1">
<Button type="submit">Save changes</Button>
</div>
</Form>
</CardContent>
</Card>
);
}Examples
Basic
A profile card with name, email, description, and a primary save action.
Update the name and email on your public account.
Settings
Username, theme select, and preference rows with Checkbox and Switch.
Control how your workspace looks and what you hear about.
Validation
Pass error strings into FormField to wire labels, controls, and alert messages.
Required fields show destructive labels and messages when invalid.
Disabled
Organization-managed fields stay readable but cannot be edited.
These fields are managed by your organization administrator.
Dashboard filters
Compose Combobox, Select, and Date Picker for dashboard filtering.
Combobox, Select, and Date Picker share the same field structure.
In practice
A workspace settings surface with Alert, Avatar, Badge, Combobox, Date Picker, Radio Group, validation, and notification rows.
Identity, visibility, ownership, and notification defaults for this workspace.
API
Other attributes for the underlying element are passed through.
| Name | Type | Default | Description |
|---|---|---|---|
Form className | string | — | Merged onto the native form. Default spacing is gap-5. |
FormField name | string | — | Optional stable id seed for the field and its helpers. |
FormField error | string | — | Validation message. When set, the field becomes invalid and FormMessage can render it. |
FormField invalid | boolean | — | Force invalid styling without providing an error string. |
FormField disabled | boolean | false | Propagates disabled to FormControl and dims the label/description. |
FormItem | div | — | Vertical stack for label, control, description, and message. Accepts layout className. |
FormLabel | Label | — | Label wired with htmlFor. Turns destructive when the field is invalid. |
FormControl | single element | — | Clones one child with id, aria-describedby, aria-invalid, data-invalid, and disabled. Wrap SelectTrigger / ComboboxTrigger for those controls. |
FormDescription | p | — | Helper copy referenced from the control via aria-describedby. |
FormMessage | p | — | Renders FormField.error or children. Uses role=alert. Returns null when empty. |
useFormField | hook | — | Reads the active field context (ids, error, invalid, disabled) from nested parts. |
Accessibility
- Each field generates stable ids.
FormLabelsetshtmlFor;FormControlappliesid,aria-describedby, andaria-invalid. - Description and message ids are only referenced while those nodes are mounted, so
aria-describedbydoes not point at missing elements. FormMessageusesrole="alert"so assistive technology announces validation text when it appears.- Invalid labels use the destructive token. Controls that support
aria-invalidpick up border emphasis from existing input styles. - Native keyboard behavior is preserved. Form does not trap focus or override Tab order.