Component

Links a label, help text and an error message to any form control, so ids, aria-describedby and aria-invalid are always right.

View component changelog
preview

We only use it to sign you in.

React
"use client" import { useState } from "react" import Field from "@radui/ui/Field" import TextField from "@radui/ui/TextField" import Button from "@radui/ui/Button" // The server says which field failed; only that field turns invalid and shows its error. export default function FieldExample() { const [error, setError] = useState<{ field: string; message: string } | null>(null) return ( <form className="flex w-full max-w-[360px] flex-col gap-4" onSubmit={(event) => { event.preventDefault() const email = String(new FormData(event.currentTarget).get("email") ?? "") setError(email.endsWith("@example.com") ? { field: "email", message: "That email already has an account." } : null) }} > <Field.Root name="email" required invalid={error?.field === "email"}> <Field.Label>Email</Field.Label> <Field.Control asChild> <TextField type="email" placeholder="ada@example.com" /> </Field.Control> <Field.Description>We only use it to sign you in.</Field.Description> <Field.Error>{error?.message}</Field.Error> </Field.Root> <Button type="submit">Create account</Button> </form> ) }
  • Links the label, help text and error to the control without hand-written ids
  • Sets aria-invalid and announces the error only while the field is invalid
  • Works with TextField, TextArea, native inputs or your own control through asChild
  • Checkbox, Switch, Select and NumberField join the field directly, without Field.Control
  • Validates with the browser's own checks plus your validate function, showing results on submit, blur or change
  • Field.Error match gives each problem its own message, such as valueMissing or typeMismatch
  • Exposes data-invalid, data-disabled, data-required, data-focused, data-touched and data-dirty on every part

Import all parts of the component and piece them together

React
import Field from "@radui/ui/Field" export default () => { return ( <Field.Root> <Field.Label /> <Field.Control /> <Field.Description /> <Field.Error /> </Field.Root> ) }

Holds the field's state and links its parts. Renders a div.

PropTypeDefault
invalid
boolean
false
validate
(value) => string | string[] | null | Promise<…>
--
validationMode
'onSubmit' | 'onBlur' | 'onChange'
'onSubmit'
disabled
boolean
false
required
boolean
false
name
string
--
asChild
boolean
false
customRootClass
string
""
validationDebounceTime
number
0
touched
boolean
--
dirty
boolean
--
actionsRef
Ref<{ validate(): boolean }>
--

Names the control. Its htmlFor points at the control automatically.

PropTypeDefault
asChild
boolean
false

The form control. Renders an input; use asChild to wire up TextField, TextArea or your own control.

PropTypeDefault
asChild
boolean
false
id
string
generated
onValueChange
(value, event) => void
--

Help text, added to the control's accessible description.

PropTypeDefault
asChild
boolean
false

The error message. Renders only while the field is invalid, is announced when it appears and is added to the control's accessible description after any help text. Without children it shows the validation message.

PropTypeDefault
match
boolean | keyof ValidityState
--
forceMount
boolean
false
asChild
boolean
false

Renders custom UI from the field's validity, such as a password checklist. Its child is a function.

PropTypeDefault
children
(validity) => ReactNode
--
ShortcutDescription
Tab

Moves focus to the control. Field adds no key handling of its own; the control keeps its native behavior.

ReferenceWhat Rad UI follows

The label names the control; help text and the error message form its accessible description through aria-describedby.

The error uses role="alert" so it is announced when it appears.