Les composants Web permettent de construire des briques d’interface réutilisables, encapsulées et compatibles avec plusieurs stacks frontend. Je vais expliquer ce qu’ils apportent vraiment, comment les construire proprement et dans quels cas je les préfère à un composant de framework classique. L’objectif est simple : vous aider à décider avec lucidité, pas à empiler une abstraction de plus.
L’essentiel à retenir pour décider vite
- Les composants Web reposent sur 3 piliers complémentaires : éléments personnalisés, Shadow DOM et gabarits avec slots.
- Ils sont particulièrement utiles pour un design system partagé, des micro-frontends ou des widgets embarqués dans plusieurs environnements.
- Le vrai sujet n’est pas seulement la technique, mais la qualité de l’API, l’accessibilité et la manière de composer le contenu.
- Je les recommande moins pour une application unique déjà très liée à un framework, où la complexité supplémentaire peut dépasser le gain.
- Le meilleur point de départ reste un élément autonome simple, bien nommé, bien testé et documenté.
Ce qu’ils apportent vraiment au frontend
Je résume souvent l’approche en trois couches. Les éléments personnalisés définissent le comportement, le Shadow DOM isole la structure et les styles, et les éléments et gèrent la composition du contenu. Comme le rappelle MDN, c’est l’association de ces briques qui rend l’ensemble intéressant, parce qu’on garde la logique dans un contrat clair entre la page et le composant.
- Custom elements pour définir de nouvelles balises et leur cycle de vie.
- Shadow DOM pour encapsuler le HTML et le CSS sans collision avec le reste de la page.
- Templates et slots pour réutiliser un gabarit et laisser le parent injecter du contenu.
Ce n’est pas un framework, et ce n’est pas non plus une simple astuce de balisage. C’est une manière de faire dialoguer HTML, CSS et JavaScript avec une séparation plus nette des responsabilités. Une fois ce cadre en tête, la vraie question devient très concrète : comment écrire un premier élément sans se perdre dans les détails inutiles ?
Construire un premier élément réutilisable
Je commence toujours par un cas d’usage minuscule. Un bon élément doit résoudre un problème réel, avoir une API courte et produire un rendu stable. Si je dois trop expliquer son fonctionnement avant même de l’utiliser, c’est souvent le signe qu’il est déjà trop ambitieux.
class UserCard extends HTMLElement {
static observedAttributes = ['name'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
render() {
const name = this.getAttribute('name') ?? 'Invité';
this.shadowRoot.innerHTML = `
${name}
Carte réutilisable.
`;
}
}
customElements.define('user-card', UserCard);Ce squelette montre l’essentiel : attachShadow() pour encapsuler, connectedCallback() pour rendre le contenu, observedAttributes pour réagir à un changement simple, et customElements.define() pour enregistrer l’élément. Ici, je simplifie volontairement le rendu ; dès que du contenu utilisateur entre dans l’équation, je passe à des nœuds DOM explicites ou à une sanitation stricte, pas à une concaténation naïve.
- Gardez l’API courte : quelques attributs clairs valent mieux qu’une poignée d’options obscures.
-
Choisissez un nom stable : en pratique, les noms en
kebab-caserendent l’usage lisible et évitent les ambiguïtés. - Exposez un comportement prévisible : un même attribut doit toujours produire le même effet.
Une fois ce squelette en place, la vraie valeur se joue dans l’encapsulation et la composition. C’est là que le Shadow DOM et les slots prennent toute leur importance.

Shadow DOM, slots et rendu serveur
Le Shadow DOM est utile, mais il n’est pas magique. Il isole les styles et la structure, ce qui évite beaucoup de collisions CSS dans les gros projets, mais il ne doit pas devenir une boîte noire impossible à personnaliser. En pratique, je préfère le mode open tant que j’ai besoin de déboguer, tester ou intégrer le composant dans plusieurs contextes ; le mode closed n’est pas un mécanisme de sécurité, seulement une barrière d’accès à l’implémentation.
Les slots servent à projeter du contenu fourni par l’appelant à l’intérieur du composant. C’est la partie qui rend l’ensemble vraiment composable. web.dev insiste d’ailleurs sur ce point : si je laisse le contenu du parent entrer proprement, le composant devient plus flexible, plus accessible et plus facile à réutiliser dans d’autres écrans.
- Le Shadow DOM protège l’intérieur du composant des styles externes.
- Les slots permettent d’injecter du contenu sans casser la structure interne.
-
Les variables CSS et
::partdonnent un contrôle de thème sans ouvrir tout le composant.
En 2026, je regarde aussi le rendu serveur avec plus d’attention qu’avant. Le Shadow DOM déclaratif permet de préparer une structure côté serveur plutôt que de tout reconstruire dans le navigateur, ce qui peut simplifier l’initialisation dans une page déjà pré-rendue. Ce n’est pas utile dans tous les cas, mais dès qu’une équipe fait du SSR sérieux, la question mérite d’être posée. Reste maintenant à savoir quand cette approche vaut mieux qu’un composant de framework classique.
Quand je les choisis plutôt qu’un framework
Le vrai arbitrage n’est pas théorique. Il dépend de votre architecture, du niveau de réutilisation attendu et du nombre de piles techniques à servir. Dans un projet mono-framework très homogène, je n’ajoute pas des composants Web par principe. En revanche, dès qu’une brique doit vivre dans plusieurs produits, ou être consommée par plusieurs équipes avec des frameworks différents, l’intérêt monte vite.
| Approche | Forces | Limites | Quand je la choisis |
|---|---|---|---|
| Composants Web natifs | Interopérables, encapsulés, indépendants du framework | API et outillage à discipliner soi-même | Design system partagé, widgets embarqués, micro-frontends |
| Composants de framework | Ergonomie, intégration d’état et de routing, vitesse d’exécution en équipe | Forte dépendance à l’écosystème | Application unique déjà standardisée sur React, Vue ou Svelte |
| HTML, CSS et JavaScript simples | Léger, direct, très lisible | Réutilisation plus limitée | Interaction courte, page isolée, besoin sans stratégie de partage |
Mon filtre est assez simple : si le composant doit sortir de l’application qui l’a vu naître, je pense native. S’il reste étroitement lié à un flux métier et à un seul runtime, je préfère la solution la plus simple possible. Le gain d’interopérabilité n’a de valeur que s’il compense le coût de conception, de documentation et de test. Ce constat mène directement à un autre point souvent sous-estimé : les bonnes pratiques qui évitent les mauvaises surprises.
Les bonnes pratiques qui évitent les surprises
Je vois les mêmes erreurs revenir dans beaucoup d’équipes : un composant visuellement propre, mais inaccessible au clavier ; une API trop verbeuse ; un événement mal nommé ; ou un état qui se synchronise dans les deux sens sans garde-fou. C’est rarement spectaculaire au début, mais cela finit presque toujours en dette technique.
- Respectez l’accessibilité par défaut : si votre composant agit comme un bouton, il doit accepter le focus, le clavier et un nom accessible.
-
Ne forcez pas les attributs globaux : si le parent a déjà défini
roleoutabindex, je les respecte au lieu d’écraser sa décision. - Exposez des attributs et des propriétés cohérents : un composant bien conçu accepte une configuration déclarative et impérative sans ambiguïté.
- Évitez les boucles d’état : quand une propriété met à jour un attribut, puis que l’attribut remet à jour la propriété, on crée vite un comportement instable.
-
Prévoyez le thème dès le départ : variables CSS,
::partet slots valent mieux qu’un CSS impossible à surcharger.
Le point que je souligne le plus souvent en revue de code est le suivant : un élément réutilisable doit être prévisible avant d’être élégant. Si l’équipe qui l’emploie doit deviner comment il réagit aux attributs, aux événements ou au focus, le coût d’usage grimpe aussitôt. Une fois ces garde-fous en place, on peut enfin parler d’un composant qui mérite d’être partagé dans une base commune.
Ce que je conseille avant de l’imposer au reste de l’équipe
Avant de transformer une brique en standard interne, je vérifie toujours quatre choses. D’abord, le besoin revient-il dans au moins deux écrans ou deux produits ? Ensuite, l’API est-elle suffisamment stable pour être documentée sans pages de mise en garde ? Puis, l’équipe sait-elle qui porte l’accessibilité, les tests et la maintenance ? Enfin, avez-vous réellement besoin d’indépendance vis-à-vis du framework, ou cherchez-vous simplement une nouveauté plus chic qu’un composant classique ?
- Oui au partage si le même pattern UI doit vivre dans plusieurs contextes techniques.
- Oui à l’encapsulation si les collisions CSS vous coûtent déjà du temps.
- Oui aux slots si le contenu doit rester flexible sans casser la structure.
- Non au surdimensionnement si une simple solution locale répond déjà au besoin.
Si je devais commencer demain, je construirais un seul élément autonome, je le testerais dans deux contextes différents, puis je mesurerais honnêtement si l’encapsulation me fait gagner plus de temps qu’elle n’en consomme. C’est souvent là que la décision devient claire : les composants Web ne sont pas une fin en soi, mais un bon outil quand la réutilisation, l’indépendance et la discipline d’interface comptent vraiment.