Form validation styles
The browser can check fields by itself using required, type="email", minlength — this was in the tutorial. But its pop-up hints look different everywhere and vanish after a couple of seconds. Bootstrap shows the result right in the form: a red border and error text under a wrong field, a green border on a correct one.
Classes for one field
The is-invalid class colors the field red and shows the invalid-feedback block that follows it. The is-valid class colors it green and shows valid-feedback:
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control is-valid" id="name" value="Anna">
<div class="valid-feedback">Looks good!</div>
</div>
<div class="mb-3">
<label for="mail" class="form-label">Email</label>
<input type="email" class="form-control is-invalid" id="mail" value="anna@">
<div class="invalid-feedback">Enter the full address, for example anna@example.com.</div>
</div>The first field has a green border and a check mark, the second a red border, an icon and the error text below. Your code sets these classes, for example when the server returns an error.
Browser validation
It is often easier to leave checking to the browser and only the display to Bootstrap. The form gets novalidate to turn off the default hints, and after a submit attempt it gets the was-validated class. Then fields are colored automatically by the rules from their attributes:
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<form id="signup" class="row g-3" novalidate>
<div class="col-12">
<label for="signupEmail" class="form-label">Work email</label>
<input type="email" class="form-control" id="signupEmail" required>
<div class="invalid-feedback">A valid email is required.</div>
</div>
<div class="col-12">
<label for="team" class="form-label">Team name</label>
<input type="text" class="form-control" id="team" minlength="3" required>
<div class="invalid-feedback">At least three characters.</div>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Create account</button>
</div>
</form>
<script>
const form = document.querySelector('#signup');
form.addEventListener('submit', (event) => {
event.preventDefault();
form.classList.add('was-validated');
if (form.checkValidity()) {
console.log('The form is valid and can be sent');
}
});
</script>Click "Create account" with empty fields and both turn red with error text. Fill them in and the borders turn green as you type; after another click the console.log message appears under the example. The checkValidity() method returns true when all fields pass.
How it works
Bootstrap relies on the :invalid and :valid pseudo-classes from the tutorial. A rule like .was-validated .form-control:invalid turns on the red border, and the neighboring .invalid-feedback becomes visible. That is why the error text block must come right after the field, on the same level.
Common mistake
Adding was-validated right away, in the HTML. Then the empty form is covered in red borders and errors before the first keystroke — the user is scolded without having done anything. Add the class only after a submit attempt, as in the example above.