Form Validation
Learn Bootstrap's form validation styling with .was-validated, .is-invalid/.is-valid classes, and valid/invalid feedback messages.
Introduction
A form is only as good as the feedback it gives when something goes wrong. Bootstrap builds on the browser's native HTML5 form validation (required, type="email", pattern, etc.) and layers on styling so invalid and valid fields are immediately obvious, with clear messages explaining what to fix.
- How .was-validated activates Bootstrap's validation styles.
- The difference between .is-invalid/.is-valid and .was-validated.
- How to write valid-feedback and invalid-feedback messages.
- How to trigger validation with a small JavaScript snippet.
How Bootstrap Validation Works
Bootstrap doesn't invent its own validation engine — it styles the results of the browser's built-in HTML5 Constraint Validation API. You still write normal attributes like `required`, `minlength`, or `type="email"`; Bootstrap just makes the pass/fail state visually obvious with colored borders, icons, and feedback text.
You can either let the browser show its native validation UI enhanced by Bootstrap's CSS (the .was-validated approach), or manually toggle .is-valid / .is-invalid classes yourself, e.g. from custom JavaScript or server-side validation.
HTML5 Custom Validation
Add the `novalidate` attribute to the `<form>` to suppress the browser's default validation bubbles, then use JavaScript to add the `.was-validated` class on submit. Once `.was-validated` is present, Bootstrap automatically shows red/green styling on every field based on its current `:invalid`/`:valid` state.
<form class="row g-3 needs-validation" novalidate> <div class="col-md-6"> <label for="validName" class="form-label">Name</label> <input type="text" class="form-control" id="validName" required> <div class="invalid-feedback">Please enter your name.</div> </div> <div class="col-12"> <button class="btn btn-primary" type="submit">Submit</button> </div></form>Click Run to see what this code prints.
Valid & Invalid Feedback
Place a `.invalid-feedback` (or `.valid-feedback`) element immediately after a form control. It stays hidden by default and Bootstrap automatically reveals it only when the sibling input matches the corresponding validation state.
<div class="mb-3"> <label for="validEmail" class="form-label">Email</label> <input type="email" class="form-control" id="validEmail" required> <div class="valid-feedback">Looks good!</div> <div class="invalid-feedback">Please provide a valid email.</div></div>Click Run to see what this code prints.
Manually Applying Validation States
Instead of relying on `.was-validated` and native constraints, you can apply `.is-invalid` or `.is-valid` directly to a single field — useful for server-side validation errors returned after an async form submission.
<label for="serverError" class="form-label">Username</label><input type="text" class="form-control is-invalid" id="serverError" value="taken_username"><div class="invalid-feedback">This username is already taken.</div>Click Run to see what this code prints.
Validating Select & Checkbox Fields
The same feedback pattern applies to `.form-select` and `.form-check` controls — Bootstrap styles the red/green border and shows the matching feedback text the same way it does for text inputs.
<div class="mb-3"> <label for="planSelect" class="form-label">Choose a plan</label> <select class="form-select" id="planSelect" required> <option value="">Select...</option> <option value="pro">Pro</option> </select> <div class="invalid-feedback">Please select a plan.</div></div>
<div class="form-check mb-3"> <input class="form-check-input" type="checkbox" id="agreeTerms" required> <label class="form-check-label" for="agreeTerms">Agree to terms</label> <div class="invalid-feedback">You must agree before submitting.</div></div>Click Run to see what this code prints.
Tooltip-Style Feedback
For forms with limited vertical space, swap `.invalid-feedback` / `.valid-feedback` for `.invalid-tooltip` / `.valid-tooltip` to show the message as a small floating tooltip positioned near the field instead of inline text.
<div class="position-relative mb-3"> <input type="text" class="form-control" required> <div class="invalid-tooltip">This field is required.</div></div>Click Run to see what this code prints.
JavaScript Validation Trigger
Bootstrap does not automatically add `.was-validated` for you — you need a small script that listens for the form's submit event, checks native validity, and applies the class.
(() => { 'use strict'; const forms = document.querySelectorAll('.needs-validation');
Array.from(forms).forEach((form) => { form.addEventListener('submit', (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } form.classList.add('was-validated'); }, false); });})();Click Run to see what this code prints.
Common Mistakes
- Forgetting the novalidate attribute on the form, which lets the browser's native validation bubbles show alongside Bootstrap's styling.
- Adding .invalid-feedback without a sibling input that has a matching validation state — it just stays hidden.
- Never adding the JavaScript snippet, so .was-validated is never applied and nothing appears to happen on submit.
- Applying .is-invalid permanently in markup instead of conditionally, so a field looks broken even before the user interacts with it.
Best Practices
- Always pair required/type/pattern attributes with a clear, specific invalid-feedback message.
- Use manual .is-invalid/.is-valid for server-side or async validation results.
- Keep the validation JavaScript snippet in a shared file if multiple forms need it.
- Test keyboard-only and screen-reader flows, since Bootstrap's feedback is announced via aria attributes.
- Prefer inline feedback over tooltip feedback unless space is genuinely constrained.
Frequently Asked Questions
For the .was-validated / native HTML5 approach, yes — a small script is required to add the class on submit. Manually applying .is-invalid/.is-valid does not require it.
.is-invalid targets one specific field directly. .was-validated is added to the whole <form> and lets the browser's native :valid/:invalid pseudo-classes drive styling for every field inside it.
Yes, you can call setCustomValidity() on an input in your own JavaScript, and Bootstrap's .was-validated styling will still respect it.
No, this is purely client-side visual feedback — you must always validate on the server independently, since client-side checks can be bypassed.
Key Takeaways
- Bootstrap styles native HTML5 validation rather than replacing it.
- .was-validated on the <form> activates styling based on each field's native validity.
- .is-invalid / .is-valid can be applied directly to a single field, e.g. for server errors.
- .invalid-feedback / .valid-feedback show contextual messages tied to a field's state.
- .invalid-tooltip / .valid-tooltip offer a floating alternative to inline feedback text.
- A small JavaScript snippet is required to intercept submit and add .was-validated.
Summary
Bootstrap's validation styling turns the browser's native form validation into something users can actually see and understand at a glance. With clear invalid/valid feedback messages and consistent color cues, you can dramatically cut down on failed form submissions. Next, you'll build the navbar — one of the most visible components on any site.