LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1519 min read

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.

What You Will Learn
  • 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.

Two Approaches

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>
What Renders

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>
What Renders

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>
What Renders

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>
What Renders

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>
What Renders

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);
});
})();
What Renders

Click Run to see what this code prints.

Common Mistakes

Avoid These 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.

Next Lesson →

Navbar Component