Field
A label, a description and an error, tied to a control and driven by its real validity.
Printed at the top right of the requisition.
Installation
npx shadcn@latest add https://qure-ui.qure.ai/r/field.jsonUsage
import {
Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel,
} from '@/components/ui/field'<Field name="accession">
<FieldLabel>Accession number</FieldLabel>
<Input placeholder="ACC-000000" />
<FieldDescription>Printed at the top right of the requisition.</FieldDescription>
</Field>There is no state prop. The error appears because the control is invalid, and the label goes
grey because the control is disabled — you describe the field, not its appearance.
This replaces the design system's InputField and CheckboxField. Those took a state prop
and rendered the literal string "I am a label.", so they could show an error but never
because one had happened. Anything still importing them is showing decoration, not state.
Composition
<Field> {/* owns validity, disabled, name; renders a div */}
<FieldLabel> {/* a real <label>, htmlFor wired automatically */}
<Input /> {/* any Base UI control registers itself here */}
<FieldDescription> {/* a <p>, referenced by aria-describedby */}
<FieldError /> {/* only in the DOM while the control is invalid */}
</Field>Field does not render the control. Input, Textarea (through FieldControl), Checkbox, Switch,
Select and the rest register themselves with the surrounding Field, which is what supplies the
id, the aria-describedby and the data-invalid the stylesheets key off.
Two parts are easy to mix up:
| Part | Use it when |
|---|---|
FieldControl | You want a native input the library has no wrapper for — type="date", type="file" — or you are rendering something else through render. |
FieldItem | You have several controls in one Field, and each needs its own label and description. A checkbox list, a radio group. |
FieldControl borrows Input's classes rather than restating them, so it takes the same
size prop and cannot drift from Input's look. Pass size={null} when what you are rendering
sets its own height, as a textarea does.
Validation
Type an incomplete number and tab away.
validate returns a string to fail and null to pass. validationMode decides when it runs —
onSubmit by default, which is the wrong choice for a single field on its own; onBlur tells a
radiographer that the accession number is short as they leave it, rather than after they submit.
FieldError with no match shows whatever the last validation produced. With a match it
listens to one entry of the browser's own
ValidityState, so
match="valueMissing" and match="rangeOverflow" can carry different sentences without any
JavaScript of yours running.
With a checkbox
Pulls the two most recent studies of the same modality for comparison.
De-identified before it leaves the department.
Unavailable until the patient consent form is on file.
Each row is a FieldItem — its own label, its own description, and disabled scoped to it.
This is the composition the old CheckboxField was reaching for. Note the third row: the label
and the description stay legible, because "unavailable until consent is on file" is information
and hiding the option loses it.
With a switch
Pages the on-call radiologist as soon as the model flags a study.
Sent at 07:00 local time.
A settings row puts the control at the end of the label rather than the start. Switch is the
on/off control; if the row would not read sensibly with "on" and "off" after it, you want a
Checkbox or a Toggle instead.
With a textarea
Dictated text is inserted here; edit before signing.
A bare <textarea> is not a Base UI control, so a Field around it can neither label nor
validate it. <FieldControl render={<Textarea />} size={null} /> registers it and keeps the
Textarea's own look.
Disabled
Put disabled on Field and it takes precedence over the control's own. Label, description and
control all pick up data-disabled from the same source, so they cannot disagree — which is how
a form ends up with a live-looking label over a dead input.
API Reference
Everything Base UI's Field accepts. The ones you
will reach for on Field:
Prop
Type
On FieldError:
Prop
Type
On FieldControl:
Prop
Type