FormControl
Status: stableAccessibility reviewedtenet-ui@0.4.0
import { FormControl } from 'tenet-ui';Also accepts every prop of HTMLAttributes<HTMLDivElement>.
Source: src/components/FormControl/FormControl.tsx
Usage guidelines
Section titled “Usage guidelines”When to use: every labeled form field. FormControl wraps a label, a field, and optional caption/validation, and wires the accessibility for you. Build forms by stacking FormControls in a Stack.
- Wrap text-like fields (
TextInput,Textarea,Select) in aFormControlwith aFormControl.Label. The control generates a shared id and setshtmlFor,aria-describedby,aria-invalid,required, anddisabledautomatically — don’t wire ids by hand, and never use a placeholder as the label. - Use
FormControl.Captionfor help text andFormControl.Validation variant="error"for errors (it also flips the field to invalid). Usevariant="success"for confirmation. - For booleans use
Checkbox(with its ownlabel); for one-of-many useRadioGroup+Radio(a<fieldset>/<legend>group). Don’t put these in aFormControl. - Lay out fields with
<Stack gap="normal">(or aGridfor side-by-side fields); submit with<Button variant="primary">(one per form). Spacing comes from thespacescale only. - Mark required fields with
requiredon theFormControl(it propagates to the field and marks the label). UseFormControl.Label visuallyHiddenonly for a single search-style field. - Field errors are inline
FormControl.Validation; a form-level or server error is aBanner variant="danger"above the fields, never a toast. - Never use raw
<input>,<textarea>,<select>, or a styled<div>toggle — every field has a design-system component.
Do / Don’t
Section titled “Do / Don’t”- ✅
<FormControl required><FormControl.Label>Email</FormControl.Label><TextInput type="email" /><FormControl.Validation variant="error">Enter a valid email.</FormControl.Validation></FormControl>
- ❌
<label>Email</label><input style={{ border: '1px solid #d0d7de' }} />— raw element, hardcoded border, label not associated, no error wiring.
See also: System guidelines forms (composition, validation display, widths, actions) and feedback (Validation vs Banner vs Toast vs Dialog).
Examples
Section titled “Examples”3 stories for FormControl — open in Storybook
- Text Field
components-formcontrol--text-field - Required With Caption
components-formcontrol--required-with-caption - With Error
components-formcontrol--with-error
The full matrix (all args, controls, accessibility panel) is in Storybook.
FormControl
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Override the generated id shared by the label and field. |
required | boolean | false | Marks the field required (asterisk on the label + required on the input). |
disabled | boolean | false | Disables the field. |
invalid | boolean | — | Force the invalid state. Otherwise inferred from a FormControl.Validation variant="error". |
children | ReactNode | — |
FormControl.Label
| Prop | Type | Default | Description |
|---|---|---|---|
visuallyHidden | boolean | false | Visually hide the label but keep it for screen readers. |
required | boolean | false | Injected by FormControl — renders the required asterisk. |
children | ReactNode | — |
FormControl.Caption
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — |
FormControl.Validation
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'error' | 'success'ValidationVariant | error | Tone of the message. error also drives the field's invalid state. |
children | ReactNode | — |