Bootstrap Form Validation

Validation tells users when they got a field right or wrong, using clear green and red styles. Bootstrap 5 ships two flavours: instant feedback powered by the browser, and custom feedback you control with a little JavaScript. Both use the same friendly colours and messages.

Valid and invalid states

The core idea is two feedback classes. Add .is-valid to a field to turn it green, or .is-invalid to turn it red. Alongside the field, add a message using .valid-feedback (green) or .invalid-feedback (red). The message only appears when the field carries the matching state class.

Showing feedback messages

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="mb-3">
  <label for="user" class="form-label">Username</label>
  <input type="text" class="form-control is-invalid" id="user">
  <div class="invalid-feedback">Please choose a username.</div>
</div>
<div class="mb-3">
  <label for="code" class="form-label">Referral code</label>
  <input type="text" class="form-control is-valid" id="code" value="HYR2026">
  <div class="valid-feedback">Looks good!</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Browser validation with .was-validated

For automatic checking, add the required attribute to your fields and the novalidate attribute to the <form>. A small script adds the class .was-validated when the user submits. Bootstrap then colours every field based on whether it passed the browser's built-in rules.

A self-validating form

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<form class="needs-validation" novalidate>
  <div class="mb-3">
    <label for="email" class="form-label">Email</label>
    <input type="email" class="form-control" id="email" required>
    <div class="invalid-feedback">Enter a valid email.</div>
  </div>
  <button class="btn btn-primary" type="submit">Submit</button>
</form>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

The script that triggers validation

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
  document.querySelectorAll('.needs-validation').forEach(function (form) {
    form.addEventListener('submit', function (event) {
      if (!form.checkValidity()) {
        event.preventDefault();
        event.stopPropagation();
      }
      form.classList.add('was-validated');
    });
  });
</script>
</body>
</html>

Validation classes

ClassEffect
.is-validMarks a single field as correct (green)
.is-invalidMarks a single field as wrong (red)
.valid-feedbackGreen message shown when the field is valid
.invalid-feedbackRed message shown when the field is invalid
.was-validatedAdded to a form to reveal all feedback at once
Note: The novalidate attribute stops the browser's default popup bubbles so Bootstrap can show its own styled messages instead. Never rely on client-side checks alone; always validate on the server too, since anyone can bypass browser validation.
  • Use .is-valid and .is-invalid for manual, field-by-field feedback.
  • Use required plus .was-validated for automatic browser checking.
  • Feedback messages need the matching state class to appear.
  • Always re-check submitted data on the server for security.