Field
Wire a control to its label, helper text, and validation message so assistive tech reads them as one unit. Field manages the aria-labelledby and aria-describedby relationships; Fieldset groups related fields under a legend; Form consolidates submission and validation.
A labelled field
A Field.Root stacks a Field.Label, the control, and an optional Field.Description. Drop a styled control — here an Input — inside it and its control auto-wires: the label associates (clicking it focuses the control) and the description links through aria-describedby. For a no-chrome control, use the bare Field.Control, which renders a native <input>.
We never share your address.
"use client";
import { Field, Input } from "@stridge/noctis";
export default function FieldBasic() {
return (
<Field.Root name="email" className="w-72">
<Field.Label>Email</Field.Label>
<Input.Root>
<Input.Control type="email" placeholder="you@example.com" />
</Input.Root>
<Field.Description>We never share your address.</Field.Description>
</Field.Root>
);
}
Validation
Add a Field.Error per validity rule and tie it to a ValidityState key with match (valueMissing, tooShort, …). The error renders into the DOM only while that rule fails, reads in the danger colour, and is wired to the control. Set validationMode on Field.Root (or Form) to choose when validation runs — here, on blur.
"use client";
import { Field, Input } from "@stridge/noctis";
export default function FieldValidation() {
return (
<Field.Root name="username" validationMode="onBlur" className="w-72">
<Field.Label>Username</Field.Label>
<Input.Root>
<Input.Control required minLength={3} placeholder="ada" />
</Input.Root>
<Field.Error match="valueMissing">A username is required.</Field.Error>
<Field.Error match="tooShort">Use at least 3 characters.</Field.Error>
</Field.Root>
);
}
Grouping with a fieldset
Fieldset.Root groups related fields under a shared Fieldset.Legend, so a screen reader announces the group name on each control. Use it for an address block, a set of preferences, or any cluster that reads as one section.
"use client";
import { Field, Fieldset, Input } from "@stridge/noctis";
export default function FieldFieldset() {
return (
<Fieldset.Root className="w-72">
<Fieldset.Legend>Shipping address</Fieldset.Legend>
<Field.Root name="street">
<Field.Label>Street</Field.Label>
<Input.Root>
<Input.Control placeholder="123 Eclipse Way" />
</Input.Root>
</Field.Root>
<Field.Root name="city">
<Field.Label>City</Field.Label>
<Input.Root>
<Input.Control placeholder="Nightfall" />
</Input.Root>
</Field.Root>
</Fieldset.Root>
);
}
Form submission
Form is a native <form> with consolidated error handling. It collects the values of its named fields, validates them together, and calls onFormSubmit with the collected values once valid. Pass errors keyed by field name to surface server-side validation.
"use client";
import { Button, Field, Form, Input } from "@stridge/noctis";
import { useState } from "react";
export default function FieldForm() {
const [submitted, setSubmitted] = useState<string | null>(null);
return (
<Form
className="w-72"
onFormSubmit={(values) => {
setSubmitted(JSON.stringify(values));
}}
>
<Field.Root name="name">
<Field.Label>Name</Field.Label>
<Input.Root>
<Input.Control required placeholder="Ada Lovelace" />
</Input.Root>
<Field.Error match="valueMissing">Please enter your name.</Field.Error>
</Field.Root>
<Field.Root name="role">
<Field.Label>Role</Field.Label>
<Input.Root>
<Input.Control placeholder="Engineer" />
</Input.Root>
</Field.Root>
<Button type="submit">Save</Button>
{submitted ? <p className="text-small text-muted">Submitted: {submitted}</p> : null}
</Form>
);
}
Composing controls
Field is control-agnostic, so the whole family drops into it with one grammar. Place a styled Input or Textarea inside Field.Root — its control auto-wires, so the labelling, description, and validation all follow (that's how the examples above are built). For a no-chrome control, use the bare Field.Control; pass its render to swap the native input for a <textarea>, a <select>, or another Base UI input primitive.
Anatomy
Compose a field from its parts; Fieldset and Form wrap groups of them.
Field.Root— the container for one field. Setnameto identify it on submit,validatefor custom validation,validationModeto choose when it runs, anddisabledto opt the whole field out.Field.Label— the associated label; clicking it focuses the control. SetnativeLabel={false}when the label wraps a non-<label>element.Field.Control— the bare control seam for a no-chrome field; renders a native<input>(passrenderfor any other element). For a styled field, drop anInput/Textareain instead — its control auto-wires.Field.Description— supporting text, linked viaaria-describedby.Field.Error— the validation message, shown only while the field is invalid; pair withmatchfor per-rule messages.Fieldset.Root/Fieldset.Legend— a group of related fields under one accessible legend.Form— a native<form>with consolidated validation, error reporting, and anonFormSubmitcallback.
Every part carries a data-slot (noctis-field, noctis-field-label, noctis-field-control, noctis-field-description, noctis-field-error, noctis-field-fieldset, noctis-field-fieldset-legend, noctis-field-form) for host-side styling — pair it with the Base UI state attributes (data-disabled, data-touched, data-dirty, data-invalid, data-valid, data-focused, data-filled). The wiring is RTL-aware by construction.
On surfaces
The same control re-tuned across the elevation scopes — the root canvas, an elevated panel, a menu, and a sunken well. It stays legible on every layer.
We never share your address.
We never share your address.
We never share your address.
We never share your address.
Design tokens
Generated from the component's declaration — the same graph that mints the CSS, so a variable name or its resolution default can't drift. The minted tokens are the public override seam: set one on any ancestor and every field in that region retunes — e.g. .compact { --noctis-field-gap: var(--noctis-space-1); } tightens the rhythm between a field's parts. Colour stays with the semantic roles (the error reads danger). See Customization for the full override ladder and Tokens for the whole graph.
API reference
Generated from the component's types — every prop, type, default, and description comes straight from the source. Each part gets its own table; parts that only forward to Base UI list just the props they pass through. Expand a row for the full type and description.