Field
A composable wrapper that labels, describes, and validates form controls.
API Reference
Original primitive API
Behavior, accessibility details, and low-level props are documented by Base UI.
Basic
Visible on your public profile.
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> );}Full list of component variables available for project-level overrides.
| Property | Default | Description |
|---|---|---|
| --field-color | var(--color-foreground) | Controls inherited field text color. |
| --field-control-bg | var(--color-background) | Controls `FieldControl` background. |
| --field-control-border-color | var(--color-border) | Controls `FieldControl` border color. |
| --field-control-border-color-invalid | var(--color-destructive) | Controls invalid `FieldControl` border and focus ring color. |
| --field-control-border-style | solid | Controls `FieldControl` border style. |
| --field-control-border-width | var(--border-width-sm) | Controls `FieldControl` border width. |
| --field-control-color | var(--color-foreground) | Controls `FieldControl` text color. |
| --field-control-font-size | var(--text-md) | Controls `FieldControl` font size. |
| --field-control-height | var(--size-lg) | Controls `FieldControl` minimum height. |
| --field-control-line-height | var(--line-height-text-md) | Controls `FieldControl` line height. |
| --field-control-padding-x | 0.875rem | Controls `FieldControl` horizontal padding. |
| --field-control-padding-y | 0.5rem | Controls `FieldControl` vertical padding. |
| --field-control-placeholder-color | var(--color-muted-foreground) | Controls `FieldControl` placeholder color. |
| --field-control-radius | var(--radius-md) | Controls `FieldControl` corner radius. |
| --field-control-transition | var(--transition-default) | Controls `FieldControl` state transition timing. |
| --field-control-width | 100% | Controls `FieldControl` width. |
| --field-description-color | var(--color-muted-foreground) | Controls description text color. |
| --field-description-font-size | var(--text-sm) | Controls description font size. |
| --field-description-line-height | var(--line-height-text-sm) | Controls description line height. |
| --field-disabled-opacity | var(--opacity-disabled) | Controls disabled slot opacity. |
| --field-error-color | var(--color-destructive) | Controls error text color. |
| --field-error-font-size | var(--text-sm) | Controls error font size. |
| --field-error-line-height | var(--line-height-text-sm) | Controls error line height. |
| --field-focus-ring-color | var(--color-ring) | Controls `FieldControl` focus ring color. |
| --field-focus-ring-offset | -1px | Controls `FieldControl` focus ring offset. |
| --field-focus-ring-width | var(--border-width-sm) | Controls `FieldControl` focus ring width. |
| --field-gap | var(--spacing-1) | Controls spacing between field parts. |
| --field-item-gap | var(--spacing-1) | Controls spacing inside `FieldItem`. |
| --field-label-color | var(--color-foreground) | Controls label text color. |
| --field-label-font-size | var(--text-sm) | Controls label font size. |
| --field-label-font-weight | var(--weight-medium) | Controls label font weight. |
| --field-label-gap | var(--spacing-2) | Controls spacing inside `FieldLabel`. |
| --field-label-line-height | var(--line-height-text-sm) | Controls label line height. |
| --field-max-width | none | Controls the root field max width. |
| --field-width | 100% | Controls the root field width. |
Interactive variables scoped for docs preview without changing size scale tokens.
| Property | Value | Default | Description |
|---|---|---|---|
| --field-control-bg | var(--color-background) | Controls control background. | |
| --field-control-border-color | var(--color-border) | Controls control border color. | |
| --field-control-color | var(--color-foreground) | Controls control text color. | |
| --field-control-radius | var(--radius-md) | Controls control radius. | |
| --field-description-color | var(--color-muted-foreground) | Controls description color. | |
| --field-error-color | var(--color-destructive) | Controls error text color. | |
| --field-focus-ring-color | var(--color-ring) | Controls focus ring color. | |
| --field-gap | var(--spacing-1) | Controls spacing between field parts. | |
| --field-label-color | var(--color-foreground) | Controls label text color. |
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.
Field
├─ FieldLabel
│ └─ label content / optional control composition
├─ FieldControl (or another compatible control, for example Input)
├─ FieldError[match]
└─ FieldDescription<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
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:
import {
type FieldActions,
type FieldState,
type FieldControlChangeEventDetails,
type FieldValidityState,
} from 'moduix';Examples
Custom Validation
Use validate for rules that are not covered by native input validity.
Waiting for valid value.
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> );}.helper { margin: 0; color: var(--color-muted-foreground); font-size: var(--text-sm); line-height: var(--line-height-text-sm);}Disabled
Set disabled on the root to disable the whole field and propagate the disabled state to supported controls.
This field is currently managed by your workspace.
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> );}Checkbox
Wrap checkbox controls in Field when the checkbox needs validation, description, or form naming.
Required to continue.
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> );}Radio
Use FieldItem to group individual radio rows while keeping each slot available for styling.
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> );}Switch
Compose FieldLabel with switch controls when the label should activate the control.
We send updates once per week.
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> );}Input
Use any Base UI-compatible input component inside Field; Input works with labels and validation out of the box.
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> );}Number Field
Pair Field with compound controls by linking the label with the control id.
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> );}Custom Styles
Pass className to every visible slot when styling the field with CSS Modules.
Use three to five uppercase letters.
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> );}.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);}