Skip to content
Esc
navigateopen⌘Jpreview
On this page

Field

Combine labels, controls, and help text to compose accessible form fields.

Profile

Shown on your public profile.

Contact
import * as stylex from '@stylexjs/stylex';

import {
  Field,
  FieldDescription,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSeparator,
  FieldSet,
} from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { container } from '@/lib/constants.stylex';

export default function FieldDemo() {
  return (
    <FieldSet style={styles.root}>
      <FieldLegend>Profile</FieldLegend>
      <FieldGroup>
        <Field>
          <FieldLabel htmlFor="field-name">Name</FieldLabel>
          <Input id="field-name" placeholder="Evil Rabbit" />
          <FieldDescription>Shown on your public profile.</FieldDescription>
        </Field>
        <FieldSeparator>Contact</FieldSeparator>
        <Field>
          <FieldLabel htmlFor="field-email">Email</FieldLabel>
          <Input id="field-email" type="email" placeholder="you@example.com" />
        </Field>
      </FieldGroup>
    </FieldSet>
  );
}

const styles = stylex.create({
  root: {
    width: container.sm,
  },
});

Install

npx shadcn@latest add @madeui/field

Usage

import {
  Field,
  FieldDescription,
  FieldError,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSeparator,
  FieldSet,
} from '@/components/ui/field';

Composition

<FieldSet>
  <FieldLegend />
  <FieldGroup>
    <Field>
      <FieldLabel />
      <Input />
      <FieldDescription />
      <FieldError />
    </Field>
    <FieldSeparator />
  </FieldGroup>
</FieldSet>

Validation

Give the field a name and place a Base UI control inside — the control joins the field automatically (label association, aria-describedby, aria-invalid, red label on error). Combine with Form to validate on submit, or set validationMode="onChange" per field. Custom rules go in validate:

<Field
  name="username"
  validate={(value) => (String(value).length < 2 ? 'Too short.' : null)}
>
  <FieldLabel>Username</FieldLabel>
  <Input required />
  <FieldError />
</Field>

Using react-hook-form or TanStack Form instead? See the Form pageField takes invalid and FieldError takes errors for externally controlled state.

API reference

Built on Base UI Field and Fieldset: Field renders Field.Root (all validation props forwarded: name, validate, validationMode, invalid, disabled, …), FieldLabel/FieldDescription/FieldError render the matching parts, FieldSet/FieldLegend render Fieldset parts. FieldGroup, FieldContent, FieldTitle, and FieldSeparator are layout markup.

Field

Prop Type Default Description
orientation 'vertical' | 'horizontal' 'vertical'
name string Identifies the field in form values and server errors.
validate (value, formValues) => string | string[] | null Custom validation; return message(s) or null.
invalid boolean Force the invalid state — for external form libraries.

FieldError

Prop Type Default Description
errors Array<{ message?: string }> External errors rendered directly (de-duplicated; one message as text, several as a list; nothing when empty). Without it, the surrounding field’s own validation message renders.
match boolean | keyof ValidityState When to show children (Base UI). true always shows — for external control.

FieldLegend accepts variant: 'legend' (base size) or 'label' (sm). FieldSeparator accepts children rendered centered over the line. All parts accept a style prop (StyleXStyles, merged last).

Was this page helpful?