Skip to content

Now in the official shadcn registry directory.

Start free

Field

A form field composition: label, control, help text and validation error in one consistent layout. FieldControl wires id, aria-describedby and aria-invalid automatically.

form
A free styled version is available

Used only for deploy alerts.

Installation

Run the following command

npx shadcn@latest add @ai2/field

Dependencies, the @ai2/tokens theme and the component file are installed together. Field depends on @ai2/label, which installs alongside it.

Usage

TS
import {
  Field,
  FieldControl,
  FieldDescription,
  FieldLabel,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"

<Field>
  <FieldLabel>Work email</FieldLabel>
  <FieldControl>
    <Input type="email" placeholder="you@company.com" />
  </FieldControl>
  <FieldDescription>Used only for deploy alerts.</FieldDescription>
</Field>

Wrap the control in FieldControl and the wiring is automatic: the label points at the control, the description and error are announced via aria-describedby, and aria-invalid follows the field state. No manual htmlFor / id pairs needed.

Examples

With description

Shown on the public dashboard.

The description is linked to the control through aria-describedby, so screen readers read it after the label.

With error

A rendered FieldError marks the whole field invalid: the control gets aria-invalid (which triggers the danger border on ai2 inputs) and the label turns danger. FieldError renders nothing when its children are empty, so you can pass your form library's error message directly.

Invalid and disabled

The invalid prop flips the field into the error look without a message. The disabled prop dims the label; pass disabled to the control as well to actually disable it.

Horizontal orientation

orientation="horizontal" puts the label and control on one row with space between, the layout used for switch settings rows. Clicking the label toggles the switch thanks to the automatic association.

Form composition

Markdown is supported.

Props

Field adds three props of its own, listed below. FieldControl is a radix Slot: it renders no DOM, takes exactly one child and forwards every prop to it. FieldDescription and FieldError accept native <p> props, and FieldLabel accepts everything the ai2 Label does. FieldError renders role="alert" and returns null without children.

PropTypeDefaultDescription
orientation"vertical" | "horizontal""vertical"Layout direction: vertical stacks label, control and messages; horizontal puts label and control on one row with space between, for switch rows.
invalidbooleanfalseMarks the whole field invalid: the control inside FieldControl gets aria-invalid and the label turns danger. A FieldError with content sets this automatically.
disabledbooleanfalseDims the label via the group chain. It does not disable the control itself; pass disabled to the control too.

ai2 Field: form field anatomy for React, one consistent stack

The ai2 Field is a shadcn-compatible form field composition for React, styled with Tailwind CSS v4 on the shared ai2 tokens. It gives every form control the same anatomy: a label above the input, optional help text and a validation error below, all stacked in a single vertical layout with a consistent gap. An orientation prop switches to a horizontal row for switch-style settings. Wrap the control in FieldControl and the accessibility wiring, id, aria-describedby and aria-invalid, is handled for you.

Because it ships through the shadcn registry format, you install it with one CLI command, an MCP agent, or a copy-paste, and the source lands in your own project. You own the file; there is no runtime dependency on ai2 itself. The live example above is the exact component you get.

What is the ai2 Field?

It is a five-part composition: Field, FieldControl, FieldLabel, FieldDescription and FieldError. The root is a flex container, a column by default or a row with space between when orientation="horizontal". FieldControl is a Slot: it renders no extra DOM and injects a generated id, an aria-describedby pointing at the description and error, and aria-invalid when the field is invalid, straight onto your control. FieldLabel points its htmlFor at that same id automatically, and FieldError renders with role="alert" in the danger color.

Field is deliberately unopinionated about the control itself: put an input, textarea, select, switch or a custom widget inside FieldControl. The parts are plain elements with data-slot attributes, so form libraries like react-hook-form plug in without adapters, and styling stays on your token file in both light and dark mode.

Why use it

  • One anatomy for every form: Label above the control, description and error below, always with the same gap. Forms across your app stop drifting apart visually.
  • Accessibility wiring on autopilot: FieldControl injects a generated id, aria-describedby for the description and error, and aria-invalid when the field is invalid. FieldLabel picks up the id on its own; no manual htmlFor and id pairs.
  • Errors announced to screen readers: FieldError renders role="alert", so assistive technology announces validation messages the moment they appear, and its presence marks the whole field invalid automatically.
  • Form-library friendly: FieldError returns null when its children are empty, so you can pass errors.email?.message straight from react-hook-form or zod without conditional JSX. When the message appears, the control gets aria-invalid for free.
  • Agent-readable metadata: The registry item describes the five parts and their intended order in plain words, so an MCP agent can compose correct forms without guessing.

Features

  • shadcn registry install: One command adds Field, its @ai2/label dependency and the @ai2/tokens theme to your project.
  • Zero-DOM control wiring: FieldControl is a radix Slot, so it adds no wrapper element. The id, aria-describedby and aria-invalid attributes land directly on your Input, Select trigger or Switch.
  • Two orientations, one gap: The Field root is a flex column with a consistent gap, and orientation="horizontal" turns it into a label-left, control-right row for switch settings.
  • Invalid and disabled field state: The invalid prop (or a rendered FieldError) sets data-invalid on the root, turns the label danger and puts aria-invalid on the control. The disabled prop dims the label through the group chain.
  • Conditional error rendering: FieldError renders nothing without children: no empty paragraphs, no layout shift logic in your form code. Its presence alone flips the field into the invalid state.
  • Data attributes for styling: Every part exposes data-slot (field, field-control, field-label, field-description, field-error), and the root carries data-orientation, data-invalid and data-disabled for targeted CSS overrides without forking.

Production tips

  • Wrap the control in FieldControl: The label association, description announcement and invalid state all flow through FieldControl. Skip it and you are back to wiring htmlFor, id and aria-describedby by hand.
  • Let FieldError drive the invalid state: Rendering a FieldError with content marks the field invalid on its own: the control gets aria-invalid and the label turns danger. Use the invalid prop only when there is no visible message.
  • Disable the control and the field together: The disabled prop only dims the label. Pass disabled to the control inside FieldControl as well so the whole row reads and behaves as disabled.
  • Keep descriptions short and stable: FieldDescription is for persistent hints like formats and limits. Do not swap it for the error text; render both parts and let FieldError appear below.
  • Compose forms with a parent gap: Stack multiple Fields inside a form with flex flex-col gap-5 or similar; the inner gap-2 handles spacing within each field.
  • Reserve role="alert" for real errors: FieldError announces immediately to assistive technology. Use it for validation failures only, not for success notes or hints.

Works with the rest of ai2

Field is the glue layer of the form kit. Put an ai2 Input or ai2 Textarea inside FieldControl for text entry, an ai2 Select for choices, and rely on the built-in ai2 Label that FieldLabel wraps.

For page-level composition, group related Fields inside an Card to give a settings section its own surface, or split long forms into collapsible sections. Everything shares one token source, so forms stay visually consistent in both modes.

FAQs

Get new components in your inbox

Component drops, new themes and release notes. No spam, and you can unsubscribe from any email.

© 2026 ai2 design system · MIT licensed · Built with its own components