# Field (/docs/field)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/field" />

## Basic [#basic]

<Preview cssProperties="fieldPlaygroundCssProperties">
  <FieldExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldControl,
            FieldDescription,
            FieldError,
            FieldLabel,
          } from "moduix";

          export function FieldDemo() {
            return (
              <Field validationMode="onBlur">
                <FieldLabel>Name</FieldLabel>
                <FieldControl required placeholder="Enter your name" />
                <FieldError match="valueMissing">Please enter your name.</FieldError>
                <FieldDescription>Visible on your public profile.</FieldDescription>
              </Field>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSSProperties>
    {(context) => <FieldCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <FieldCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`Field` acts as a semantic wrapper around one form control and its helper text. Keep label,
control, error, and description inside the same `Field` so IDs, validation state, and ARIA links
stay synchronized.

```text
Field
├─ FieldLabel
│  └─ label content / optional control composition
├─ FieldControl (or another compatible control, for example Input)
├─ FieldError[match]
└─ FieldDescription
```

```tsx
<Field validationMode="onBlur">
  <FieldLabel>Email</FieldLabel>
  <FieldControl required type="email" placeholder="name@example.com" />
  <FieldError match="valueMissing">Please enter your email.</FieldError>
  <FieldError match="typeMismatch">Enter a valid email address.</FieldError>
  <FieldDescription>We will only use this for account notifications.</FieldDescription>
</Field>
```

| Part               | Role                                                                                            |
| ------------------ | ----------------------------------------------------------------------------------------------- |
| `Field`            | Root context. Coordinates validation mode, disabled state, naming, and slot relationships.      |
| `FieldLabel`       | Accessible label for the field control. Can wrap composed controls like `Switch` or `Checkbox`. |
| `FieldControl`     | Default text-like control slot that receives invalid/disabled styling hooks.                    |
| `FieldError`       | Validation message slot filtered by `match` (for example `valueMissing`, `customError`).        |
| `FieldDescription` | Supplemental helper text linked to the control for assistive technologies.                      |
| `FieldItem`        | Optional layout wrapper for grouped rows inside the same field, such as radio options.          |

`Field` does not have service slots such as `portal`, `backdrop`, or `viewport`.
In most cases, keep default structure and style only visible slots (`FieldLabel`, `FieldControl`,
`FieldError`, `FieldDescription`, and optional `FieldItem`) via `className` and CSS variables.

## Composition [#composition]

Use `Field` for root behavior and validation props such as `name`, `validationMode`,
`validationDebounceTime`, `validate`, `actionsRef`, `invalid`, `dirty`, `touched`, and `disabled`.

Compose `FieldLabel`, one compatible control (`FieldControl`, `Input`, `NumberField`, and others),
then optional `FieldError` and `FieldDescription`. Customize each visible slot through `className`.
All Base UI parts also support `render`, function `className`, function `style`, and state data
attributes such as `data-invalid`, `data-valid`, `data-dirty`, `data-touched`, `data-filled`,
`data-focused`, and `data-disabled`.

Use `FieldControl` for a default native input, or omit it and place another Base UI-compatible
control in the field. `FieldLabel` supports `nativeLabel={false}` when the label is rendered as a
non-label element for controls such as select or combobox triggers. `FieldError match` accepts
`true` or a `ValidityState` key such as `valueMissing`, `typeMismatch`, or `customError`;
`FieldValidity` exposes the full validity object through a render function.

The package exports Base UI helper types for advanced composition:

```tsx
import {
  type FieldActions,
  type FieldState,
  type FieldControlChangeEventDetails,
  type FieldValidityState,
} from 'moduix';
```

## Examples [#examples]

### Custom Validation [#custom-validation]

Use `validate` for rules that are not covered by native input validity.

<Preview>
  <FieldCustomValidationExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldControl,
            FieldError,
            FieldLabel,
            FieldValidity,
          } from "moduix";

          export function CustomValidationFieldDemo() {
            return (
              <Field
                validationMode="onChange"
                validate={(value) => {
                  if (typeof value !== "string" || value.length < 3) {
                    return "Username must be at least 3 characters.";
                  }

                  return null;
                }}
              >
                <FieldLabel>Username</FieldLabel>
                <FieldControl placeholder="e.g. Vinny" />
                <FieldError match="customError" />
                <FieldValidity>
                  {(state) => (
                    <p className={styles.helper}>
                      {state.validity.valid ? "Looks good." : "Waiting for valid value."}
                    </p>
                  )}
                </FieldValidity>
              </Field>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .helper {
            margin: 0;
            color: var(--color-muted-foreground);
            font-size: var(--text-sm);
            line-height: var(--line-height-text-sm);
          }
        `}
  </Preview.CSS>
</Preview>

### Disabled [#disabled]

Set `disabled` on the root to disable the whole field and propagate the disabled state to supported controls.

<Preview>
  <FieldDisabledExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldControl,
            FieldDescription,
            FieldLabel,
          } from "moduix";

          export function DisabledFieldDemo() {
            return (
              <Field disabled>
                <FieldLabel>Organization</FieldLabel>
                <FieldControl placeholder="Acme Inc." />
                <FieldDescription>
                  This field is currently managed by your workspace.
                </FieldDescription>
              </Field>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Checkbox [#checkbox]

Wrap checkbox controls in `Field` when the checkbox needs validation, description, or form naming.

<Preview>
  <FieldCheckboxExample />

  <Preview.Code>
    {`
          import {
            Field,
            Checkbox,
            CheckboxIndicator,
            FieldDescription,
            FieldError,
            FieldLabel,
          } from "moduix";

          export function CheckboxFieldDemo() {
            return (
              <Field validationMode="onBlur">
                <FieldLabel>
                  <Checkbox required name="terms">
                    <CheckboxIndicator />
                  </Checkbox>
                  I agree to the terms
                </FieldLabel>
                <FieldError match="valueMissing">Please accept the terms.</FieldError>
                <FieldDescription>Required to continue.</FieldDescription>
              </Field>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Radio [#radio]

Use `FieldItem` to group individual radio rows while keeping each slot available for styling.

<Preview>
  <FieldRadioExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldError,
            FieldItem,
            FieldLabel,
            Radio,
            RadioField,
            RadioGroup,
            RadioLabel,
          } from "moduix";

          export function RadioFieldDemo() {
            return (
              <Field name="account-type" validationMode="onBlur">
                <FieldLabel>Account type</FieldLabel>
                <RadioGroup>
                  <FieldItem>
                    <RadioField>
                      <Radio value="personal" required />
                      <RadioLabel>Personal account</RadioLabel>
                    </RadioField>
                  </FieldItem>
                  <FieldItem>
                    <RadioField>
                      <Radio value="team" />
                      <RadioLabel>Team account</RadioLabel>
                    </RadioField>
                  </FieldItem>
                </RadioGroup>
                <FieldError match="valueMissing">
                  Please choose an account type.
                </FieldError>
              </Field>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Switch [#switch]

Compose `FieldLabel` with switch controls when the label should activate the control.

<Preview>
  <FieldSwitchExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldDescription,
            FieldLabel,
            Switch,
            SwitchLabel,
          } from "moduix";

          export function SwitchFieldDemo() {
            return (
              <Field name="newsletter">
                <FieldLabel>
                  <Switch />
                  <SwitchLabel>Subscribe to newsletter</SwitchLabel>
                </FieldLabel>
                <FieldDescription>We send updates once per week.</FieldDescription>
              </Field>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Input [#input]

Use any Base UI-compatible input component inside `Field`; `Input` works with labels and validation out of the box.

<Preview>
  <FieldInputExample />

  <Preview.Code>
    {`
          import { Field, FieldLabel, Input, FieldError } from "moduix";

          export function InputFieldDemo() {
            return (
              <Field validationMode="onBlur">
                <FieldLabel>Email</FieldLabel>
                <Input required type="email" placeholder="name@example.com" />
                <FieldError match="valueMissing">Please enter your email.</FieldError>
                <FieldError match="typeMismatch">Enter a valid email address.</FieldError>
              </Field>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Number Field [#number-field]

Pair `Field` with compound controls by linking the label with the control id.

<Preview>
  <FieldNumberFieldExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldError,
            NumberField,
            NumberFieldDecrement,
            NumberFieldGroup,
            NumberFieldInput,
            NumberFieldIncrement,
            FieldLabel,
          } from "moduix";
          import { useId } from "react";

          export function NumberFieldDemo() {
            const id = useId();

            return (
              <Field name="quantity" validationMode="onBlur">
                <FieldLabel htmlFor={id}>Items</FieldLabel>
                <NumberField id={id} min={1} max={10} required>
                  <NumberFieldGroup>
                    <NumberFieldDecrement aria-label="Decrease value" />
                    <NumberFieldInput />
                    <NumberFieldIncrement aria-label="Increase value" />
                  </NumberFieldGroup>
                </NumberField>
                <FieldError match="valueMissing">Please provide a number.</FieldError>
                <FieldError match="rangeUnderflow">Value should be at least 1.</FieldError>
                <FieldError match="rangeOverflow">Value should be at most 10.</FieldError>
              </Field>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Custom Styles [#custom-styles]

Pass `className` to every visible slot when styling the field with CSS Modules.

<Preview>
  <FieldCustomStylesExample />

  <Preview.Code>
    {`
          import {
            Field,
            FieldControl,
            FieldDescription,
            FieldError,
            FieldLabel,
          } from "moduix";

          export function CustomStylesFieldDemo() {
            return (
              <Field validationMode="onBlur" className={styles.customField}>
                <FieldLabel className={styles.customLabel}>Project key</FieldLabel>
                <FieldControl
                  required
                  placeholder="MAPS"
                  className={styles.customControl}
                />
                <FieldDescription className={styles.customDescription}>
                  Use three to five uppercase letters.
                </FieldDescription>
                <FieldError className={styles.customError} match="valueMissing">
                  Please enter a project key.
                </FieldError>
              </Field>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customField {
            width: min(22rem, 100%);
            max-width: 20rem;
            gap: var(--spacing-2);
          }

          .customLabel,
          .customError {
            color: var(--color-primary);
          }

          .customControl {
            border-color: color-mix(in srgb, var(--color-primary) 40%, transparent);
          }

          .customControl:focus {
            outline-color: var(--color-primary);
          }

          .customDescription {
            color: var(--color-muted-foreground);
          }
        `}
  </Preview.CSS>
</Preview>
