-
Objectifs
- Comprendre le principe de la validation des formulaires en Bootstrap 5.3 (HTML5 + classes CSS)
- Être capable d'afficher des messages de validation avec
.was-validated,.valid-feedbacket.invalid-feedback - Être capable de déclencher la validation à l'envoi du formulaire avec JavaScript
- Être capable d'afficher le résultat d'une validation côté serveur avec
.is-validet.is-invalid
-
Présentation
- Voici comment fonctionne la validation de formulaire avec Bootstrap 5.3 :
- Le navigateur vérifie les règles HTML5 déclarées sur les champs (
required,type="email",minlength,pattern…). Le résultat est exposé par les deux pseudo-classes CSS:invalidet:valid, qui s'appliquent aux éléments <input>, <select> et <textarea>. - Bootstrap applique ses styles (bordure rouge ou verte, icône, message) à ces pseudo-classes uniquement à l'intérieur d'un parent portant la classe
.was-validated, en général le <form>. Sans cela, tous les champs obligatoires vides apparaîtraient en rouge dès le chargement de la page. - On ajoute donc
.was-validatedau bon moment, le plus souvent après la tentative d'envoi du formulaire, grâce à un petit script JavaScript. - Pour réinitialiser l'apparence du formulaire (par exemple après un envoi en AJAX), il suffit de retirer la classe
.was-validateddu <form>. - Pour la validation côté serveur (PHP, Symfony…), on utilise à la place les classes
.is-validet.is-invaliddirectement sur les champs. Elles ne nécessitent pas de parent.was-validated.
- Le navigateur vérifie les règles HTML5 déclarées sur les champs (
- Les messages s'affichent avec :
.valid-feedback/.invalid-feedback: texte sous le champ ;.valid-tooltip/.invalid-tooltip: info-bulle superposée (le parent doit avoirposition: relative, classe.position-relative).
- Voici comment fonctionne la validation de formulaire avec Bootstrap 5.3 :
Source : getbootstrap.com/docs/5.3/forms/validation
-
Les attributs de validation HTML5
- Bootstrap ne valide rien lui-même : il met en forme le résultat de la validation du navigateur. Les règles se déclarent donc avec les attributs HTML5 suivants :
| Attribut | Rôle | Exemple |
|---|---|---|
required |
Champ obligatoire | <input required> |
type |
Format attendu (e-mail, URL, nombre…) | type="email", type="url" |
minlength / maxlength |
Nombre minimal / maximal de caractères | minlength="8" |
min / max / step |
Bornes d'une valeur numérique ou d'une date | type="number" min="18" max="99" |
pattern |
Expression régulière à respecter | pattern="[0-9]{8}" (8 chiffres) |
-
Valider à l'envoi du formulaire (méthode recommandée)
- C'est la méthode proposée par la documentation officielle. Procédez comme suit :
- Ajoutez l'attribut
novalidateau <form>: il désactive les bulles d'erreur natives du navigateur, afin d'afficher à la place les messages de Bootstrap. - Ajoutez une classe (ex.
.needs-validation) pour que le script retrouve le formulaire. - Sous chaque champ, ajoutez un
<div class="invalid-feedback">(et éventuellement un.valid-feedback). - Ajoutez le script : à l'envoi, si le formulaire n'est pas valide (
checkValidity()), on bloque l'envoi ; dans tous les cas, on ajoute.was-validatedpour afficher les styles.
- Ajoutez l'attribut
- C'est la méthode proposée par la documentation officielle. Procédez comme suit :
<form class="row g-3 needs-validation" novalidate>
<div class="col-md-6">
<label for="nom" class="form-label">Nom</label>
<input type="text" class="form-control" id="nom" required>
<div class="valid-feedback">Parfait !</div>
<div class="invalid-feedback">Veuillez saisir votre nom.</div>
</div>
<div class="col-md-6">
<label for="email" class="form-label">E-mail</label>
<input type="email" class="form-control" id="email" required>
<div class="invalid-feedback">Veuillez saisir une adresse e-mail valide.</div>
</div>
<div class="col-md-6">
<label for="mdp" class="form-label">Mot de passe</label>
<input type="password" class="form-control" id="mdp" minlength="8" required>
<div class="invalid-feedback">Au moins 8 caractères.</div>
</div>
<div class="col-md-6">
<label for="tel" class="form-label">Téléphone</label>
<input type="tel" class="form-control" id="tel" pattern="[0-9]{8}" required>
<div class="invalid-feedback">Le numéro doit contenir 8 chiffres.</div>
</div>
<div class="col-md-6">
<label for="filiere" class="form-label">Filière</label>
<select class="form-select" id="filiere" required>
<option selected disabled value="">Choisir...</option>
<option value="web">Développement web</option>
<option value="mobile">Développement mobile</option>
</select>
<div class="invalid-feedback">Veuillez choisir une filière.</div>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="conditions" required>
<label class="form-check-label" for="conditions">J'accepte les conditions</label>
<div class="invalid-feedback">Vous devez accepter avant d'envoyer.</div>
</div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Envoyer</button>
</div>
</form>
<script>
(() => {
'use strict';
const forms = document.querySelectorAll('.needs-validation');
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault(); // bloque l'envoi
event.stopPropagation();
}
form.classList.add('was-validated'); // affiche les styles
}, false);
});
})();
</script>
- Remarque : la case à cocher et son message
.invalid-feedbacksont placés dans le.form-check, mais en dehors du<label>.
-
La classe .was-validated en statique
- Si l'on place directement
.was-validatedsur le <form>, les styles de validation s'affichent dès le chargement de la page, en temps réel pendant la saisie. - C'est pratique pour une démonstration, mais déconseillé en production : l'utilisateur voit des erreurs avant même d'avoir commencé.
- Si l'on place directement
<div class="container">
<h4>Validation d'un formulaire</h4>
<p>Dans cet exemple, nous utilisons <code>.was-validated</code> pour indiquer ce qui manque avant l'envoi du formulaire :</p>
<form class="was-validated">
<div class="mb-3">
<label for="uname" class="form-label">Nom d'utilisateur :</label>
<input type="text" class="form-control" id="uname" placeholder="Entrez le nom d'utilisateur" name="uname" required>
<div class="valid-feedback">Valide.</div>
<div class="invalid-feedback">Veuillez remplir ce champ.</div>
</div>
<div class="mb-3">
<label for="pwd" class="form-label">Mot de passe :</label>
<input type="password" class="form-control" id="pwd" placeholder="Entrez le mot de passe" name="pswd" required>
<div class="valid-feedback">Valide.</div>
<div class="invalid-feedback">Veuillez remplir ce champ.</div>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" id="accord" name="remember" required>
<label class="form-check-label" for="accord">Je suis d'accord.</label>
<div class="valid-feedback">Valide.</div>
<div class="invalid-feedback">Cochez cette case pour continuer.</div>
</div>
<button type="submit" class="btn btn-primary">Soumettre</button>
</form>
</div>
- Ce qui nous donne :

-
Validation côté serveur : .is-valid et .is-invalid
- La validation dans le navigateur peut être contournée : il faut toujours revérifier les données côté serveur (PHP, Symfony…).
- Après ce contrôle, le serveur renvoie le formulaire en ajoutant
.is-invalid(ou.is-valid) sur les champs concernés, avec le message d'erreur dans un.invalid-feedback. - Pour l'accessibilité, reliez le message au champ avec
aria-describedby.
<form>
<div class="mb-3">
<label for="login" class="form-label">Nom d'utilisateur</label>
<input type="text" class="form-control is-invalid" id="login" value="admin" aria-describedby="loginErreur">
<div id="loginErreur" class="invalid-feedback">Ce nom d'utilisateur est déjà pris.</div>
</div>
<div class="mb-3">
<label for="courriel" class="form-label">E-mail</label>
<input type="email" class="form-control is-valid" id="courriel" value="etudiant@exemple.com">
<div class="valid-feedback">Adresse disponible.</div>
</div>
<button type="submit" class="btn btn-primary">Envoyer</button>
</form>
-
Messages sous forme d'info-bulles (tooltips)
- Remplacez
.valid-feedback/.invalid-feedbackpar.valid-tooltip/.invalid-tooltippour afficher le message dans une bulle. - Le parent du champ doit avoir la classe
.position-relative, sinon la bulle est mal placée.
- Remplacez
<form class="row g-3 needs-validation" novalidate>
<div class="col-md-6 position-relative">
<label for="ville" class="form-label">Ville</label>
<input type="text" class="form-control" id="ville" required>
<div class="invalid-tooltip">Veuillez indiquer votre ville.</div>
</div>
<div class="col-12 mt-5">
<button class="btn btn-primary" type="submit">Envoyer</button>
</div>
</form>
-
Validation dans un groupe de saisie (input-group)
- Dans un
.input-group, ajoutez la classe.has-validationpour que les coins arrondis restent corrects quand le message s'affiche.
- Dans un
<label for="pseudo" class="form-label">Pseudo</label>
<div class="input-group has-validation">
<span class="input-group-text">@</span>
<input type="text" class="form-control" id="pseudo" required>
<div class="invalid-feedback">Veuillez choisir un pseudo.</div>
</div>
-
Réinitialiser le formulaire
- Après un envoi réussi en AJAX, on vide les champs et on retire
.was-validatedpour effacer les couleurs et les messages.
- Après un envoi réussi en AJAX, on vide les champs et on retire
const form = document.querySelector('.needs-validation');
form.reset(); // vide les champs
form.classList.remove('was-validated'); // efface les styles de validation
-
Schéma de principe

- En résumé : attributs HTML5 (règles) → navigateur (
:valid/:invalid) → .was-validated ajoutée à l'envoi → styles et messages Bootstrap → contrôle final côté serveur (.is-valid/.is-invalid).
-
Récapitulatif des classes
| Classe / attribut | Où | Rôle |
|---|---|---|
novalidate |
<form> |
Désactive les bulles natives du navigateur |
.was-validated |
<form> |
Active l'affichage des styles de validation |
.is-valid / .is-invalid |
Champ | Force l'état valide / invalide (validation serveur) |
.valid-feedback / .invalid-feedback |
Après le champ | Message sous le champ |
.valid-tooltip / .invalid-tooltip |
Après le champ | Message en info-bulle (parent .position-relative) |
.has-validation |
.input-group |
Garde des coins arrondis corrects avec un message |
-
Bonnes pratiques
- N'ajoutez
.was-validatedqu'après la première tentative d'envoi. - Écrivez des messages précis qui disent comment corriger (« Au moins 8 caractères » plutôt que « Champ invalide »).
- Pour un
<select>obligatoire, la première option doit avoirvalue="", sinon elle est considérée comme une valeur valide. - La validation côté navigateur améliore le confort ; seule la validation côté serveur garantit la sécurité.
- N'ajoutez