Validation
Communicate the success or failure of a field. A control carries the
isValid / isInvalid state; a sibling
FormFeedback shows the matching message. It’s a sibling-driven
contract — the control owns the class and the feedback reacts to it.
Import
import { FormControl, FormFeedback } from '@metatoy/bootstrap-styled'; Valid & invalid feedback
Set isValid or isInvalid on the control — it emits the .is-valid /
.is-invalid class and the green/red border. Place a FormFeedback with the
matching type immediately after it, as a sibling; the feedback is hidden until
the preceding sibling is marked, then reveals.
Tooltip feedback
Add tooltip to FormFeedback for an absolutely-positioned overlay instead of a
block message that reflows the layout. Give the wrapping element
position: relative so the tooltip anchors to the field.
Browser-driven validation
Add the was-validated class to the <form> and the browser’s native :valid /
:invalid pseudo-classes drive the feedback — no isValid / isInvalid props
needed. Use noValidate on the form so the browser shows your styled messages
instead of its own bubbles.
A live validated form
Drive isValid / isInvalid from your own state to validate on submit. Here the
control stays unmarked until the form is submitted, then reflects whether the
email matches:
Props
FormFeedback
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "valid" | "invalid" | — | Valid (green) or invalid (red) feedback. Default `invalid`. |
| tooltip | boolean | — | Render the tooltip variant (absolutely positioned overlay) instead of the inline block message. |
FormFeedback also accepts all native <div> attributes. The isValid /
isInvalid state lives on the control — see FormControl and FormSelect.
Theming
Feedback and control states read the dedicated validation tokens:
--bs-form-valid-color / --bs-form-invalid-color for the message text,
--bs-form-valid-border-color / --bs-form-invalid-border-color for the control
border, and --bs-success / --bs-danger for the tooltip fills. Override them
to match your palette:
<div style={{ '--bs-form-invalid-color': '#b91c1c', '--bs-danger': '#dc2626' }}>
<FormControl isInvalid defaultValue="nope" aria-label="Username" />
<FormFeedback type="invalid">Please choose a valid username.</FormFeedback>
</div> Accessibility
Color alone can’t carry the valid/invalid meaning, so always pair the state with
a text FormFeedback message. Wire the control to its message with
aria-describedby and set aria-invalid when a field is in error so assistive
tech announces the problem. Keep submit-time validation focused: move focus to
the first invalid control so keyboard and screen-reader users land on it.