Quand on apprend le HTML, on découvre un ensemble de balises bien connues : <div>
, <p>
, <header>
, <footer>
, <button>
, etc. Chacune a un rôle précis, défini par les standards du web. Mais tôt ou tard, tout développeur web ressent une frustration : celle de ne pas pouvoir créer ses propres balises : les custom element, avec un comportement spécifique et un style intégré.
Imaginez pouvoir écrire dans votre code :
<user-profile></user-profile>
… et que cette balise affiche automatiquement une carte de profil complète avec une photo, un nom, et un bouton de contact. C’est exactement ce que permettent les Custom Elements, une technologie du HTML5 qui change profondément notre façon de structurer les pages web.
Avec les Custom Elements, vous pouvez inventer vos propres balises HTML, leur donner un comportement dynamique, et les réutiliser sur tous vos projets sans dépendre d’un framework lourd. C’est une approche moderne, puissante, et surtout standardisée : autrement dit, compatible avec tous les navigateurs modernes et intégrée au cœur du langage.
Dans ce guide, nous allons découvrir ensemble ce que sont les Custom Elements, comment les créer, comment les styliser et comment les utiliser dans de vrais projets.
Nous prendrons le temps de comprendre chaque étape, avec des exemples concrets et des explications claires, afin que même si vous débutez en JavaScript ou en développement front-end, vous puissiez les utiliser en toute autonomie.
- Comprendre les Web Components et les Custom Elements
- Créer son premier Custom Element
- Comprendre le cycle de vie d’un Custom Element
- Isoler le style avec le Shadow DOM
- Créer des composants dynamiques et réactifs
- Réutiliser ses composants avec les modules JavaScript
- Compatibilité, performances et accessibilité
- Vers une bibliothèque de composants personnalisés
- Bonnes pratiques de développement
- Le futur du HTML est déjà là
Comprendre les Web Components et les Custom Elements
Avant de plonger dans le code, il est essentiel de comprendre le contexte dans lequel s’inscrivent les Custom Elements. Ces derniers font partie d’un ensemble plus large appelé Web Components, un standard du W3C qui vise à rendre le web plus modulaire et réutilisable.
Qu’est-ce qu’un Web Component ?
Un Web Component est une brique autonome du web, qui combine du HTML, du CSS et du JavaScript dans un seul élément réutilisable. L’idée est simple : au lieu de copier-coller les mêmes blocs de code dans toutes vos pages, vous créez un composant que vous pouvez utiliser comme une nouvelle balise.
Prenons un exemple concret. Si vous développez souvent des fiches produits pour un site e-commerce, vous pouvez créer une balise personnalisée comme :
<product-card></product-card>
Une fois ce composant défini, il affichera toujours la même structure, le même style, et le même comportement. Vous n’aurez plus qu’à l’inclure dans vos pages, sans répéter le code HTML et CSS à chaque fois.
Les quatre technologies des Web Components
Les Web Components reposent sur quatre technologies principales qui travaillent main dans la main :
- Custom Elements : permettent de créer vos propres balises HTML et de définir leur comportement avec JavaScript.
- Shadow DOM : isole le style et la structure interne du composant du reste de la page.
- HTML Templates : permettent de définir une structure HTML réutilisable, sans qu’elle soit directement visible dans la page.
- ES Modules : facilitent l’organisation et la réutilisation du code JavaScript entre plusieurs fichiers.
Dans ce guide, nous nous concentrerons principalement sur les Custom Elements, tout en découvrant progressivement comment le Shadow DOM et les templates HTML peuvent les renforcer.
Pourquoi utiliser des Custom Elements ?
Les avantages sont nombreux, surtout si vous avez déjà travaillé sur des projets complexes ou réutilisables.
- Réutilisabilité : un composant peut être utilisé dans plusieurs pages ou projets sans duplication de code.
- Encapsulation : vous pouvez isoler le style et le comportement d’un composant pour éviter les conflits avec le reste du site.
- Standardisation : contrairement à React, Vue ou Angular, les Custom Elements ne nécessitent aucun framework externe. Ils font partie du langage HTML lui-même.
- Interopérabilité : ils peuvent être utilisés dans n’importe quel projet, qu’il soit en pur HTML ou intégré à un framework existant.
En d’autres termes, les Custom Elements représentent une évolution naturelle du HTML. Ils permettent d’écrire du code plus clair, plus maintenable et plus proche du concept de “composant”, très populaire dans le développement moderne.
Créer son premier Custom Element
Il est maintenant temps de passer à la pratique. Nous allons créer ensemble un premier composant simple pour bien comprendre le fonctionnement de base.
Préparer la structure du projet
Créez un dossier de test sur votre ordinateur, par exemple :custom-elements-demo
À l’intérieur, créez deux fichiers :
index.html
script.js
Voici le contenu minimal de votre fichier index.html
:
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon premier Custom Element</title>
</head>
<body>
<h1>Découvrons les Custom Elements</h1>
<!-- Notre future balise personnalisée -->
<user-card></user-card>
<script src="script.js"></script>
</body>
</html>
Vous remarquez la balise <user-card>
?
Elle n’existe pas encore. C’est justement ce que nous allons créer dans le fichier script.js
.
Créer une classe pour le composant
Ouvrez le fichier script.js
et ajoutez le code suivant :
class UserCard extends HTMLElement {
constructor() {
super();
this.innerHTML = `
<div style="border:1px solid #ccc; padding:10px; border-radius:8px; width:200px;">
<h2>Jean Dupont</h2>
<p>Développeur Web</p>
</div>
`;
}
}
Ce que vous venez de faire est très simple :
- Vous avez créé une classe JavaScript nommée
UserCard
. - Cette classe hérite de
HTMLElement
, la classe de base de toutes les balises HTML. - Dans le constructeur, vous définissez le contenu de la balise grâce à
this.innerHTML
.
Enregistrer la balise dans le navigateur
Pour que votre navigateur reconnaisse votre nouvelle balise <user-card>
, vous devez l’enregistrer grâce à la méthode customElements.define()
:
customElements.define('user-card', UserCard);
Le premier argument est le nom de votre balise personnalisée (obligatoirement en minuscules et contenant un tiret), et le second est la classe qui définit son comportement.
En rechargeant votre page index.html
, vous devriez voir apparaître une petite carte avec le nom “Jean Dupont”. Félicitations, vous venez de créer votre premier Custom Element !
Rendre le composant plus flexible
Actuellement, votre balise affiche toujours les mêmes données. Pour la rendre plus utile, vous pouvez la rendre dynamique en utilisant des attributs HTML.
Modifiez votre script.js
ainsi :
class UserCard extends HTMLElement {
constructor() {
super();
const name = this.getAttribute('name') || 'Utilisateur inconnu';
const role = this.getAttribute('role') || 'Rôle non défini';
this.innerHTML = `
<div style="border:1px solid #ccc; padding:10px; border-radius:8px; width:200px;">
<h2>${name}</h2>
<p>${role}</p>
</div>
`;
}
}
customElements.define('user-card', UserCard);
Vous pouvez maintenant écrire dans votre HTML :
<user-card name="Alban Leroy" role="Développeur Front-End"></user-card>
<user-card name="Sophie Martin" role="Graphiste"></user-card>
Chaque balise affichera ses propres informations. Votre composant est désormais réutilisable et personnalisable, tout en restant extrêmement léger.
Comprendre le cycle de vie d’un Custom Element
Lorsqu’un navigateur charge une page contenant un Custom Element, celui-ci passe par plusieurs étapes successives, appelées callbacks du cycle de vie.
Ces fonctions spéciales vous permettent d’exécuter du code automatiquement à des moments clés : lorsque l’élément est ajouté au DOM, lorsqu’il est supprimé, lorsque ses attributs changent, ou encore lorsqu’il est déplacé.
Comprendre et maîtriser ce cycle est fondamental pour créer des composants vraiment intelligents, capables de s’adapter à leur environnement sans que vous ayez besoin d’intervenir manuellement.
Les quatre callbacks essentiels
Un Custom Element peut définir quatre fonctions principales, que le navigateur appellera automatiquement :
connectedCallback()
→ Appelée lorsque le composant est inséré dans la page.
C’est ici que vous initialisez votre contenu, attachez des écouteurs d’événements ou chargez des données.disconnectedCallback()
→ Appelée quand l’élément est retiré du DOM.
Vous pouvez y supprimer les écouteurs d’événements ou nettoyer la mémoire.attributeChangedCallback(name, oldValue, newValue)
→ Appelée à chaque fois qu’un attribut du composant change.
Cela permet de rendre le composant réactif à ses propriétés HTML.adoptedCallback()
→ Appelée si l’élément est déplacé d’un document à un autre (rare, mais utile pour certaines applications avancées).
Exemple pratique : un compteur interactif
Pour bien comprendre ces fonctions, créons un exemple concret : un compteur cliquable, sous forme de balise personnalisée.
Nous allons créer un fichier counter.js
contenant le code suivant :
class ClickCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.button = document.createElement('button');
this.button.textContent = `Cliqué ${this.count} fois`;
}
connectedCallback() {
this.appendChild(this.button);
this.button.addEventListener('click', () => {
this.count++;
this.button.textContent = `Cliqué ${this.count} fois`;
});
}
disconnectedCallback() {
console.log('Le compteur a été retiré du DOM');
}
}
customElements.define('click-counter', ClickCounter);
Et dans votre index.html
:
<script src="counter.js"></script>
<click-counter></click-counter>
Rechargez la page : chaque clic sur le bouton incrémente le compteur. Simple, efficace, et entièrement contenu dans une seule balise <click-counter>
.
Grâce à la fonction connectedCallback()
, le code d’initialisation du composant ne s’exécute que lorsqu’il est inséré dans le DOM. Ainsi, même si vous créez la balise en JavaScript après le chargement de la page, elle fonctionnera immédiatement.
Réagir aux changements d’attributs
Un composant ne doit pas seulement être interactif : il doit aussi être réactif.
Autrement dit, si vous modifiez ses attributs dans le HTML ou via JavaScript, il doit automatiquement mettre à jour son affichage.
Pour cela, nous utilisons la fonction attributeChangedCallback()
.
Prenons un exemple simple avec un élément <custom-title>
dont la couleur change selon un attribut color
.
class CustomTitle extends HTMLElement {
constructor() {
super();
this.innerHTML = `<h2>${this.getAttribute('text') || 'Titre par défaut'}</h2>`;
}
static get observedAttributes() {
return ['text', 'color'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'text') {
this.querySelector('h2').textContent = newValue;
}
if (name === 'color') {
this.querySelector('h2').style.color = newValue;
}
}
}
customElements.define('custom-title', CustomTitle);
Et dans votre HTML :
<custom-title text="Bienvenue" color="blue"></custom-title>
<script>
const title = document.querySelector('custom-title');
setTimeout(() => {
title.setAttribute('text', 'Bienvenue sur mon site !');
title.setAttribute('color', 'green');
}, 2000);
</script>
Deux secondes après le chargement, le texte et la couleur changent automatiquement.
Le navigateur détecte la modification des attributs et appelle attributeChangedCallback()
sans que vous ayez à rafraîchir la page.
C’est exactement ce type de mécanisme qui rend les Custom Elements puissants et autonomes.

Des formations informatique pour tous !
Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…
Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.
Découvrez mes formations Qui suis-je ?Isoler le style avec le Shadow DOM
Un autre avantage majeur des Custom Elements est la possibilité d’utiliser le Shadow DOM.
C’est une technologie qui permet de créer un DOM caché, isolé du reste de la page. Le Shadow DOM empêche les styles externes d’affecter votre composant, et inversement. Il garantit donc une apparence stable, quel que soit l’environnement où vous intégrez votre balise.
Pourquoi utiliser le Shadow DOM ?
Sans Shadow DOM, vos composants héritent du style global du site. Cela peut poser problème si vous intégrez votre composant dans des projets différents : la couleur, la taille ou la police peuvent varier d’un site à l’autre.
Le Shadow DOM résout ce problème en créant un espace privé. Les styles définis à l’intérieur ne s’appliquent qu’au contenu du composant, et ne “fuitent” pas dans le reste du document.
Exemple d’un composant avec style isolé
Modifions notre précédent <user-card>
pour lui donner un style plus soigné et isolé.
class UserCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const container = document.createElement('div');
container.classList.add('card');
container.innerHTML = `
<h2>${this.getAttribute('name')}</h2>
<p>${this.getAttribute('role')}</p>
`;
const style = document.createElement('style');
style.textContent = `
.card {
border: 2px solid #0d6efd;
border-radius: 10px;
padding: 15px;
width: 220px;
background-color: #f9f9f9;
font-family: Arial, sans-serif;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
}
.card h2 {
color: #0d6efd;
margin: 0;
}
.card p {
color: #555;
}
`;
shadow.append(style, container);
}
}
customElements.define('user-card', UserCard);
Et dans votre HTML :
<user-card name="Alice Martin" role="Designer UX"></user-card>
Cette fois, le style du composant est totalement isolé. Même si vous modifiez la feuille de style principale de votre site, les couleurs et la mise en forme du <user-card>
ne changeront pas.
Comprendre le mode “open” et “closed”
Lorsque vous créez un Shadow DOM avec this.attachShadow({ mode: 'open' })
, vous autorisez le JavaScript externe à accéder au contenu de l’ombre via element.shadowRoot
.
Si vous utilisez mode: 'closed'
, le contenu devient totalement privé : aucune interaction n’est possible depuis l’extérieur. En général, le mode open est recommandé pour le développement classique, car il facilite le débogage et les manipulations.
Créer des composants dynamiques et réactifs
Maintenant que vous savez comment isoler le style, vous pouvez rendre vos Custom Elements interactifs et connectés à des données.
Prenons un exemple pratique : une balise <weather-box>
qui affiche la météo d’une ville.
class WeatherBox extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
async connectedCallback() {
const city = this.getAttribute('city') || 'Paris';
const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=48.85&longitude=2.35¤t_weather=true`;
const response = await fetch(apiUrl);
const data = await response.json();
const weather = data.current_weather;
this.shadowRoot.innerHTML = `
<style>
.weather {
border: 1px solid #ccc;
padding: 15px;
border-radius: 8px;
background-color: #eef;
width: 220px;
}
h3 { margin: 0 0 10px 0; color: #004080; }
</style>
<div class="weather">
<h3>Météo à ${city}</h3>
<p>Température : ${weather.temperature}°C</p>
<p>Vent : ${weather.windspeed} km/h</p>
</div>
`;
}
}
customElements.define('weather-box', WeatherBox);
Puis dans votre HTML :
<weather-box city="Paris"></weather-box>
En un seul tag HTML, vous obtenez un petit widget météo autonome. C’est exactement la puissance des Custom Elements : des composants réutilisables, stylisés, dynamiques et isolés.
Réutiliser ses composants avec les modules JavaScript
Lorsque vous commencez à créer plusieurs Custom Elements, le code de votre fichier script.js
risque rapidement de devenir difficile à gérer. Pour garder votre projet propre et organisé, il est conseillé d’utiliser les modules JavaScript, une fonctionnalité moderne qui permet de séparer le code en plusieurs fichiers et de le réutiliser facilement.
Pourquoi utiliser les modules ?
Les modules vous permettent de :
- Mieux structurer votre projet, en plaçant chaque composant dans son propre fichier.
- Réutiliser vos Custom Elements sur plusieurs sites ou applications.
- Éviter les conflits de variables et les pollutions globales.
- Charger du code à la demande, ce qui améliore les performances.
C’est le même principe que dans des frameworks comme React ou Vue, mais directement intégré dans le JavaScript natif.
Exemple d’organisation modulaire
Imaginons que vous ayez deux composants :
UserCard
(une carte de profil)ClickCounter
(un compteur cliquable)
Vous pouvez les organiser ainsi :
/custom-elements/
│
├── components/
│ ├── user-card.js
│ └── click-counter.js
│
└── index.html
Dans chaque fichier composant, vous définissez et exportez votre classe :
user-card.js
export class UserCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const container = document.createElement('div');
container.innerHTML = `
<h2>${this.getAttribute('name')}</h2>
<p>${this.getAttribute('role')}</p>
`;
const style = document.createElement('style');
style.textContent = `
div {
border: 1px solid #ccc;
border-radius: 8px;
padding: 10px;
font-family: Arial, sans-serif;
}
h2 { color: #0d6efd; }
p { color: #555; }
`;
shadow.append(style, container);
}
}
customElements.define('user-card', UserCard);
click-counter.js
export class ClickCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
this.button = document.createElement('button');
this.button.textContent = `Cliqué ${this.count} fois`;
this.shadowRoot.append(this.button);
}
connectedCallback() {
this.button.addEventListener('click', () => {
this.count++;
this.button.textContent = `Cliqué ${this.count} fois`;
});
}
}
customElements.define('click-counter', ClickCounter);
Et enfin, dans votre fichier index.html :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Bibliothèque de Custom Elements</title>
</head>
<body>
<user-card name="Alban Leroy" role="Développeur Web"></user-card>
<click-counter></click-counter>
<script type="module">
import './components/user-card.js';
import './components/click-counter.js';
</script>
</body>
</html>
En utilisant l’attribut type="module"
, vous autorisez votre navigateur à charger des fichiers JavaScript modulaires. Dès lors, chaque composant peut être maintenu et amélioré indépendamment.
Cette approche facilite également le partage de votre travail : vous pouvez créer votre propre bibliothèque de composants HTML réutilisables et l’importer dans n’importe quel projet, tout comme une librairie externe.
Compatibilité, performances et accessibilité
Les Custom Elements sont une technologie moderne, mais il est important de connaître leurs limites, leur compatibilité, et quelques bonnes pratiques pour garantir une expérience utilisateur optimale.
Compatibilité navigateur
Les Custom Elements sont pris en charge par tous les navigateurs modernes :
- Chrome, Edge, Firefox, Safari, Opera, et même la plupart des navigateurs mobiles récents.
Pour les versions plus anciennes (notamment Internet Explorer 11), vous pouvez utiliser des polyfills comme ceux disponibles sur le dépôt webcomponents.org. Un polyfill est une petite bibliothèque qui ajoute la compatibilité d’une fonctionnalité récente sur un navigateur qui ne la supporte pas nativement.
En pratique, à moins que vous ne développiez pour un environnement très ancien, les Custom Elements fonctionnent partout.
Performance et encapsulation
Grâce au Shadow DOM, vos composants ne génèrent pas de conflit de style ni de surcharge inutile. Cependant, quelques conseils permettent de garder de bonnes performances :
- Évitez les opérations lourdes dans le constructeur ou le
connectedCallback()
.
Préférez un chargement asynchrone ou différé si nécessaire. - Ne surchargez pas vos composants de styles internes si vous en avez beaucoup :
le Shadow DOM duplique les feuilles de style pour chaque instance. - Utilisez des balises légères et bien nommées. Un Custom Element doit avoir une fonction claire et isolée.
Accessibilité et sémantique
Créer ses propres balises ne doit pas faire oublier l’accessibilité. Les Custom Elements ne sont pas reconnus comme des balises HTML sémantiques par défaut. Il est donc essentiel d’y intégrer des rôles ARIA ou de reproduire la sémantique native.
Par exemple, si vous créez un <custom-button>
, pensez à imiter le comportement d’un vrai <button>
:
class CustomButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const btn = document.createElement('button');
btn.textContent = this.getAttribute('label') || 'Cliquez ici';
this.shadowRoot.append(btn);
}
connectedCallback() {
this.setAttribute('role', 'button');
this.setAttribute('tabindex', '0');
this.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
this.click();
}
});
}
}
customElements.define('custom-button', CustomButton);
Ce petit ajout garantit que votre composant reste accessible aux claviers et lecteurs d’écran. En somme, même si vos balises sont “nouvelles”, elles doivent respecter les mêmes bonnes pratiques que les balises HTML standard.
Vers une bibliothèque de composants personnalisés
Une fois que vous maîtrisez la création, la modularisation et l’isolation de vos Custom Elements, vous pouvez aller plus loin : créer votre propre design system.
Un design system est une collection cohérente de composants réutilisables (boutons, cartes, formulaires, barres de navigation, etc.), conçus avec la même logique visuelle et technique. Grâce aux Custom Elements, ce système peut être 100 % standard HTML, sans dépendance à un framework.
Vous pouvez par exemple construire :
<app-header>
pour la barre de navigation.<app-footer>
pour le pied de page.<user-card>
pour les profils.<comment-box>
pour un système de commentaires.<rating-stars>
pour afficher des étoiles de notation.
Ces composants peuvent être intégrés dans vos futurs projets web, dans des sites vitrines, des applications, voire des outils internes. Vous les codez une fois, vous les utilisez partout.
Bonnes pratiques de développement
Avant de conclure, voici quelques conseils issus de l’expérience de nombreux développeurs :
- Nommer correctement vos balises : un Custom Element doit contenir un tiret (
-
), par exemple<user-card>
ou<app-header>
.
C’est une règle imposée par le standard HTML pour éviter tout conflit avec de futures balises officielles. - Toujours utiliser le Shadow DOM dès que vous avez du style personnalisé. Cela évite les conflits CSS entre vos composants et le reste du site.
- Séparer la logique, le style et la structure à l’aide des templates et modules.
- Limiter la complexité d’un composant : un Custom Element doit remplir une seule fonction claire.
- Prévoir la réutilisation : écrivez vos composants de manière générique (via des attributs ou des propriétés), plutôt que figés dans un seul contexte.
Ces principes vous aideront à bâtir des composants robustes, durables et compatibles avec tous les environnements.
Le futur du HTML est déjà là
Les Custom Elements représentent une véritable révolution silencieuse dans le monde du développement web. Ils redonnent au HTML son pouvoir originel : celui d’être un langage simple, extensible et universel.
Là où, hier encore, il fallait recourir à des frameworks complexes pour obtenir des composants réutilisables, il est désormais possible de le faire avec les outils natifs du navigateur.
Grâce aux Custom Elements, vous pouvez créer vos propres balises, leur donner un style et un comportement unique, tout en gardant la compatibilité totale avec le reste du web. Ils favorisent la clarté du code, la réutilisation et la pérennité : trois piliers essentiels d’un développement moderne.
Le plus passionnant est que cette technologie s’inscrit pleinement dans la philosophie du web ouvert. Vous n’avez pas besoin d’installer quoi que ce soit, ni d’utiliser un framework particulier. Vous écrivez du HTML, du CSS et du JavaScript — mais à un niveau supérieur, plus structuré, plus durable.
Apprendre à maîtriser les Custom Elements, c’est apprendre à penser en composants, à construire un web plus modulaire, plus accessible et plus performant.
Alors, pourquoi ne pas commencer dès maintenant à créer votre propre bibliothèque de balises HTML personnalisées?
Le futur du HTML est déjà là, et il n’attend plus que vous pour prendre forme.