React Bootstrap Form Validation
React Bootstrap form validation in AdminKit: the browser's constraint checks with Bootstrap feedback, a was-validated class from state, and server errors.
Validation tells people what is wrong with a form before it is sent, right next to the field. AdminKit for React uses the browser's own checks: the constraints you put on a field, such as required, type="email", minLength and pattern. Bootstrap's validation classes then show the result. There is no validation library and no component: a few lines of state set .was-validated on the form. It works in the free and PRO versions of AdminKit for React, and the PRO demo shows it on the Validation page.
Example
noValidate turns off the browser's own messages. When the form is submitted, checkValidity() checks every field. .was-validated on the form then makes Bootstrap show the :valid and :invalid styles and the feedback texts.
import { type FormEvent, useState } from "react";
import { cx } from "@/lib/cx";
export function SignupForm() {
const [validated, setValidated] = useState(false);
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setValidated(true);
if (!event.currentTarget.checkValidity()) {
return;
}
const data = new FormData(event.currentTarget);
// send data to your API
};
return (
<form className={cx("row g-3 needs-validation", validated && "was-validated")} noValidate onSubmit={onSubmit}>
<div className="col-md-6">
<label htmlFor="validationFirstName" className="form-label">First name</label>
<input type="text" className="form-control" id="validationFirstName" name="firstName" defaultValue="Mark" required />
<div className="valid-feedback">Looks good!</div>
</div>
<div className="col-md-6">
<label htmlFor="validationCity" className="form-label">City</label>
<input type="text" className="form-control" id="validationCity" name="city" required />
<div className="invalid-feedback">Please provide a valid city.</div>
</div>
<div className="col-12">
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="validationTerms" required />
<label className="form-check-label" htmlFor="validationTerms">Agree to terms and conditions</label>
<div className="invalid-feedback">You must agree before submitting.</div>
</div>
</div>
<div className="col-12">
<button className="btn btn-primary" type="submit">Submit form</button>
</div>
</form>
);
}cx from src/lib/cx.ts joins the class names and drops false. The demo's CustomValidation component only calls preventDefault() when the form is invalid. The browser then submits a valid form the normal way and reloads the page. In an app, prevent the default every time and send the data yourself, as above.
Classes
| Class | Element | Description |
|---|---|---|
was-validated | <form> | Shows the :valid and :invalid styles of every field in it |
is-valid, is-invalid | field | Marks one field, without .was-validated |
valid-feedback, invalid-feedback | <div> after the field | The message, shown for the matching state |
valid-tooltip, invalid-tooltip | <div> after the field | The message as a tooltip; the parent needs position-relative |
Feedback works for .form-control, .form-select and .form-check. In an .input-group, put the feedback inside the group, after the input.
Browser defaults
For a simple form, the browser's built-in messages may be enough. Without noValidate, the browser blocks the submit of an invalid form and shows its own message bubble. This needs no state at all:
<form className="row g-3">
<div className="col-md-8">
<label htmlFor="defaultEmail" className="form-label">Email</label>
<input type="email" className="form-control" id="defaultEmail" required />
</div>
<div className="col-12">
<button className="btn btn-primary" type="submit">Submit form</button>
</div>
</form>Errors from your API
Some checks only your server can do, such as "this username is taken". Keep the errors it returns in state, and set .is-invalid on the fields:
<div>
<label htmlFor="serverUsername" className="form-label">Username</label>
<input
type="text"
className={cx("form-control", errors.username && "is-invalid")}
id="serverUsername"
name="username"
defaultValue="admin"
aria-invalid={Boolean(errors.username)}
aria-describedby="serverUsernameFeedback"
/>
<div id="serverUsernameFeedback" className="invalid-feedback">{errors.username}</div>
</div>Accessibility
- Link each feedback text to its field with
aria-describedby, so screen readers read the message with the field. - Bootstrap's classes are visual only. Set
aria-invalidon fields you mark with.is-invalid, as in the example above. - After a failed submit, move the focus to the first invalid field, for example with
form.querySelector<HTMLElement>(":invalid")?.focus(). - Do not rely on color alone: keep a feedback text for every invalid state.
Related
- Next.js Bootstrap form validation: the same validation in AdminKit for Next.js
- Bootstrap 5 validation: validation in the HTML version
- Form controls: inputs, textareas and reading a form
- Validation demo
Last updated on
Date picker
A React Bootstrap datepicker with Vanilla Calendar Pro in AdminKit: an inline calendar, and in PRO a date, range and time picker on a form input.
Layout
React Bootstrap form layout examples for AdminKit: stacked, grid, horizontal and inline forms, and input groups with text, buttons and a dropdown, in JSX.