Skip to content

FormControl

Status: stableAccessibility reviewedtenet-ui@0.4.0
Import
import { FormControl } from 'tenet-ui';

Also accepts every prop of HTMLAttributes<HTMLDivElement>.

Source: src/components/FormControl/FormControl.tsx

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 a FormControl with a FormControl.Label. The control generates a shared id and sets htmlFor, aria-describedby, aria-invalid, required, and disabled automatically — don’t wire ids by hand, and never use a placeholder as the label.
  • Use FormControl.Caption for help text and FormControl.Validation variant="error" for errors (it also flips the field to invalid). Use variant="success" for confirmation.
  • For booleans use Checkbox (with its own label); for one-of-many use RadioGroup + Radio (a <fieldset>/<legend> group). Don’t put these in a FormControl.
  • Lay out fields with <Stack gap="normal"> (or a Grid for side-by-side fields); submit with <Button variant="primary"> (one per form). Spacing comes from the space scale only.
  • Mark required fields with required on the FormControl (it propagates to the field and marks the label). Use FormControl.Label visuallyHidden only for a single search-style field.
  • Field errors are inline FormControl.Validation; a form-level or server error is a Banner 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.
  • <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).

3 stories for FormControl — open in Storybook

The full matrix (all args, controls, accessibility panel) is in Storybook.

FormControl

PropTypeDefaultDescription
idstringOverride the generated id shared by the label and field.
requiredbooleanfalseMarks the field required (asterisk on the label + required on the input).
disabledbooleanfalseDisables the field.
invalidbooleanForce the invalid state. Otherwise inferred from a FormControl.Validation variant="error".
childrenReactNode

FormControl.Label

PropTypeDefaultDescription
visuallyHiddenbooleanfalseVisually hide the label but keep it for screen readers.
requiredbooleanfalseInjected by FormControl — renders the required asterisk.
childrenReactNode

FormControl.Caption

PropTypeDefaultDescription
childrenReactNode

FormControl.Validation

PropTypeDefaultDescription
variant'error' | 'success'
ValidationVariant
errorTone of the message. error also drives the field's invalid state.
childrenReactNode