Field
Wraps a single control with its label, helper text and error message, and wires them together — htmlFor, aria-describedby, aria-invalid, required and disabled — through context.
We only use it for receipts.
<Field label="Email" description="We only use it for receipts.">
<Input type="email" placeholder="you@company.com" />
</Field>Import
import { Field } from "@merid/react";Examples
Error
When error is set, the control gets aria-invalid="true" and the invalid style, and the message is appended to its aria-describedby. Pass undefined, null or false to clear it.
That username is taken.
<Field label="Username" error="That username is taken." required>
<Input defaultValue="ahmet" />
</Field>Controlled input
Field does not own the value. Control the child as usual and derive error from your state.
Shown in the sidebar.
const [name, setName] = useState("");
const error = name.length > 0 && name.length < 3 ? "Use at least 3 characters." : undefined;
<Field label="Project name" description="Shown in the sidebar." error={error}>
<Input value={name} onChange={(event) => setName(event.target.value)} />
</Field>Uncontrolled input
With defaultValue and name, the control keeps its own state and submits with the form.
<Field label="Bio">
<Textarea name="bio" defaultValue="Designer in Istanbul." />
</Field>Disabled
disabled disables the control and dims the label.
<Field label="Plan" disabled>
<NativeSelect defaultValue="team">
<option value="free">Free</option>
<option value="team">Team</option>
</NativeSelect>
</Field>Which controls read Field
Input, Textarea and NativeSelect read the full wiring. Props set on the control itself always win over the Field's.
- Checkbox is named by the Field label. Its own children still render as a clickable label but become its description (
aria-describedby), so it is never double-labelled. - Switch takes the Field id (so the Field label names it),
aria-describedby,aria-invalidanddisabled. - RadioGroup is named by the Field label (
aria-labelledby) and picks up its description, error,requiredanddisabled.
Every control follows the same convention: aria-invalid carries the semantics and data-invalid drives the styling.
API reference
Field renders a <div>, forwards its ref and passes other div attributes through.
| Prop | Type | Default | Description |
|---|---|---|---|
labelRequired | ReactNode | – | Visible label, wired to the control via htmlFor. |
childrenRequired | ReactNode | – | A single Merid control; it picks up the wiring from context. |
description | ReactNode | – | Helper text below the control, linked via aria-describedby. |
error | ReactNode | – | Error message. Sets aria-invalid on the control and links the message. |
required | boolean | false | Marks the control required and shows the visual marker. |
disabled | boolean | false | Disables the control and dims the label. |
id | string | – | Explicit id for the control. Generated when omitted. |
For custom controls, useFieldContext() returns the wiring (controlId, labelId, descriptionId, errorId, invalid, required, disabled) or null outside a Field.
Styling
| Hook | Values |
|---|---|
.mrd-field | Root <div> |
.mrd-field__description | Helper text |
.mrd-field__error | Error message |
data-invalid | Present when error is set |
data-disabled | Present when disabled |
The label is a Label (.mrd-label).
Accessibility
- The label names the control; the description and error are read after it through
aria-describedby. - The error is not a live region. When validating on submit, move focus to the first invalid control so the message is announced.
requiredis set on the control, so it is announced and native validation applies.
Guidelines
Do
Write errors that say how to fix the problem: “Use at least 3 characters.”
Avoid
Show “Invalid input”, or put more than one control inside a Field.
Related
- Label — the label Field renders.
- Input, Textarea, NativeSelect, Checkbox, Switch, RadioGroup — controls that read Field.