logo oujood
🔍

Web Components : construire un composant natif complet en HTML5

Custom Elements, Shadow DOM, Templates et Slots — trois technologies qui fonctionnent séparément, mais qui donnent leur pleine mesure ensemble. Ce tutoriel construit de A à Z un composant <carte-notification> réutilisable, encapsulé et 100% natif, sans jQuery ni framework.

OUJOOD.COM

Les trois briques des Web Components

HTML5 API – mise à jour 2026

Les Web Components regroupent trois technologies natives du navigateur. Chacune est utilisable seule, mais c'est leur combinaison qui produit un vrai composant autonome :

  • Custom Elements : on définit une nouvelle balise HTML avec sa propre classe JavaScript.
  • Shadow DOM : on lui attache un DOM privé — les styles restent à l'intérieur, la page n'y touche pas.
  • Templates & Slots : on écrit la structure HTML une seule fois dans un <template>, et les <slot> laissent entrer le contenu depuis l'extérieur.

Ce tutoriel construit un composant <carte-notification> complet en quatre étapes — template, classe JavaScript, utilisation HTML, instanciation dynamique. Aucun npm, aucun bundler, aucun framework.


Ce que nous allons construire

Le composant <carte-notification> affiche un message avec quatre niveaux de sévérité : info, succès, avertissement, erreur. Il expose trois slots nommés — icône, titre, message — que l'utilisateur du composant remplit librement. La couleur de la carte change selon l'attribut type, sans aucun JavaScript de rendu supplémentaire grâce au sélecteur CSS :host.

  📋 Copier le code

<carte-notification type="succes">
  <span slot="icone">✅</span>
  <span slot="titre">Enregistrement réussi</span>
  <span slot="message">Vos données ont bien été sauvegardées.</span>
</carte-notification>

<carte-notification type="erreur">
  <span slot="icone">❌</span>
  <span slot="titre">Connexion échouée</span>
  <span slot="message">Vérifiez votre identifiant et mot de passe.</span>
</carte-notification>
  

Étape 1 — Définir le template HTML

On écrit la structure du composant dans un <template>. Il contient le CSS encapsulé via :host, trois slots nommés et la mise en page de la carte. Tant que le Custom Element ne le clone pas, ce template reste invisible et inactif dans la page.

  📋 Copier le code

<template id="tpl-notification">
  <style>
    :host                { display: block; margin-bottom: 1rem; }
    :host([type="info"]) { --couleur: #0066cc; --fond: #e8f0fe; }
    :host([type="succes"])      { --couleur: #1a7f3c; --fond: #e6f4ea; }
    :host([type="avertissement"]) { --couleur: #b45309; --fond: #fef9c3; }
    :host([type="erreur"])      { --couleur: #c0392b; --fond: #fdecea; }

    .carte {
      display: flex;
      align-items: flex-start;
      gap: 0.8rem;
      padding: 1rem 1.2rem;
      border-radius: 8px;
      border-left: 4px solid var(--couleur, #0066cc);
      background: var(--fond, #e8f0fe);
      font-family: sans-serif;
    }
    .icone  { font-size: 1.4rem; line-height: 1; }
    .corps  { display: flex; flex-direction: column; gap: 0.2rem; }
    .titre  { font-weight: bold; color: var(--couleur, #0066cc); }
    .message { color: #333; font-size: 0.95rem; }
  </style>

  <div class="carte">
    <div class="icone"><slot name="icone">ℹ️</slot></div>
    <div class="corps">
      <div class="titre"><slot name="titre">Notification</slot></div>
      <div class="message"><slot name="message">Aucun message.</slot></div>
    </div>
  </div>
</template>
  

Le sélecteur :host cible l'élément hôte depuis l'intérieur du Shadow DOM — ici <carte-notification> lui-même. :host([type="succes"]) applique des styles conditionnels selon la valeur de l'attribut type, sans toucher au JavaScript.


Étape 2 — Créer le Custom Element

La classe observe l'attribut type. Dans connectedCallback, elle attache un Shadow DOM, récupère le template et le clone dedans. Le sélecteur :host dans le CSS réagit seul aux changements de type — pas besoin de re-rendre le composant.

  📋 Copier le code

class CarteNotification extends HTMLElement {

  static get observedAttributes() {
    return ['type'];
  }

  connectedCallback() {
    // Attacher le Shadow DOM
    const shadow = this.attachShadow({ mode: 'open' });

    // Cloner le template et l'injecter dans le shadow root
    const template = document.getElementById('tpl-notification');
    shadow.appendChild(template.content.cloneNode(true));
  }

  attributeChangedCallback(nom, ancienne, nouvelle) {
    // Le sélecteur :host réagit automatiquement — pas besoin de re-render
    console.log(`Attribut "${nom}" changé : ${ancienne} → ${nouvelle}`);
  }
}

customElements.define('carte-notification', CarteNotification);
  

Étape 3 — Utiliser le composant dans la page

Le composant est enregistré. On l'utilise avec des contenus et des types différents — le Shadow DOM garantit que rien dans la feuille de style globale ne peut l'affecter :

  📋 Copier le code

<!-- Notification d'information -->
<carte-notification type="info">
  <span slot="icone">ℹ️</span>
  <span slot="titre">Mise à jour disponible</span>
  <span slot="message">Une nouvelle version est prête à être installée.</span>
</carte-notification>

<!-- Notification de succès -->
<carte-notification type="succes">
  <span slot="icone">✅</span>
  <span slot="titre">Profil mis à jour</span>
  <span slot="message">Vos modifications ont bien été enregistrées.</span>
</carte-notification>

<!-- Notification d'avertissement -->
<carte-notification type="avertissement">
  <span slot="icone">⚠️</span>
  <span slot="titre">Session expirée bientôt</span>
  <span slot="message">Vous serez déconnecté dans 5 minutes.</span>
</carte-notification>

<!-- Notification d'erreur -->
<carte-notification type="erreur">
  <span slot="icone">❌</span>
  <span slot="titre">Paiement refusé</span>
  <span slot="message">Vérifiez les informations de votre carte bancaire.</span>
</carte-notification>
  

Étape 4 — Instancier le composant dynamiquement en JavaScript

document.createElement('carte-notification') crée le composant comme n'importe quelle balise HTML. On remplit les slots via innerHTML, on fixe l'attribut type avec setAttribute(), et on l'insère dans la page. Ici on ajoute aussi une suppression automatique après 5 secondes :

  📋 Copier le code

function afficherNotification(type, icone, titre, message) {
  const carte = document.createElement('carte-notification');
  carte.setAttribute('type', type);

  // Créer et injecter les slots
  carte.innerHTML = `
    <span slot="icone">${icone}</span>
    <span slot="titre">${titre}</span>
    <span slot="message">${message}</span>
  `;

  document.getElementById('notifications').appendChild(carte);

  // Supprimer automatiquement après 5 secondes
  setTimeout(() => carte.remove(), 5000);
}

// Appel de la fonction
afficherNotification('succes', '✅', 'Fichier envoyé', 'Votre fichier a été uploadé avec succès.');
  

Récapitulatif : les trois technologies en un coup d'œil

Dans <carte-notification>, chaque technologie a un rôle précis. Custom Elements : la classe CarteNotification enregistre la balise et observe l'attribut type. Shadow DOM : attachShadow({ mode: 'open' }) isole les styles via :host. Template & Slots : le <template id="tpl-notification"> structure la mise en page, les trois <slot> nommés reçoivent icône, titre et message depuis l'extérieur.


Web Components vs frameworks JavaScript

Web Components ne remplace pas React ou Vue sur des applications complexes — mais dans certains contextes, c'est le bon outil :

  • Zéro dépendance : aucun package npm, aucun bundler. Un fichier HTML et un fichier JS suffisent.
  • Interopérabilité totale : un Web Component fonctionne dans React, Vue, Angular ou un simple fichier HTML sans modification.
  • Pérennité : les standards du navigateur bougent lentement — un composant écrit aujourd'hui fonctionnera dans dix ans.
  • Performance directe : pas de virtual DOM, pas de couche d'abstraction — le navigateur manipule le DOM réel.

Par carabde | Mis à jour le 17 avril 2026


chapitre précédent   sommaire   chapitre suivant