![]() |
Nello sviluppo frontend moderno, l’esigenza di creare interfacce modulari, riutilizzabili e interoperabili è sempre più forte. In questo contesto, i Web Components si propongono come una soluzione standard, potente e agnostica rispetto ai framework, per affrontare problemi di incapsulamento, duplicazione e manutenzione del codice UI.
In questo articolo vedremo:
- Cosa sono i Web Components e come funzionano
- Perché sono utili in scenari reali
- Un caso d’uso concreto adottato da GitHub
- Un esempio pratico completo con codice e benefici architetturali
🔍 Cosa sono i Web Components?
I Web Components sono una tecnologia nativa dei browser che permette di creare componenti HTML personalizzati, incapsulati e riutilizzabili, senza dipendere da framework esterni.
Sono basati su quattro specifiche fondamentali:
- Custom Elements – Definizione di nuovi tag HTML tramite JavaScript (
customElements.define()). - Shadow DOM – Incapsulamento dello stile e del markup per evitare conflitti con il resto della pagina.
- HTML Templates – Template HTML riutilizzabili che non vengono renderizzati fino all’uso.
- ES Modules – Supporto a moduli JavaScript per l’organizzazione e l’importazione del codice.
Esempio base:
class MyButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
button { background: teal; color: white; padding: 10px; border: none; border-radius: 4px; }
</style>
<button><slot></slot></button>
`;
}
}
customElements.define('my-button', MyButton);
Uso:
<my-button>Clicca qui</my-button>
🎯 Perché usarli: vantaggi e problemi risolti
| Problema | Soluzione offerta dai Web Components |
|---|---|
| Conflitti di stile e JavaScript | Shadow DOM per isolamento completo |
| Codice duplicato in progetti multipli | Componenti riutilizzabili ovunque |
| Lock-in su framework | Standard web nativi e agnostici |
| Integrazione difficile in architetture microfrontend | Componenti interoperabili |
I Web Components sono ideali per:
- Design system cross-framework
- Applicazioni legacy
- Microfrontend e widget distribuiti
- Sviluppo condiviso tra team diversi
🏢 Caso reale: GitHub e Web Components
GitHub ha adottato Web Components per migliorare la modularità e scalabilità della UI. Esempi reali includono:
<relative-time>per mostrare date relative<markdown-toolbar>per l’editor di testo<details-dialog>per finestre modali
Motivazioni:
- Gestione modulare del codice in un monolite esistente
- Compatibilità tra team e ambienti diversi
- Manutenzione semplificata dei componenti
Anche Apple, Adobe e Salesforce usano Web Components per creare design system scalabili e componenti cross-app.
🧪 Esempio pratico: Feedback Widget riutilizzabile
🎯 Requisito
In un’organizzazione con varie app (React, Angular, Vanilla JS), serve un componente uniforme per raccogliere feedback utente:
- Interfaccia compatta e coerente
- Invio asincrono a un endpoint
- Nessun conflitto di stile
- Facilità di integrazione in tutti gli ambienti
🛠️ Implementazione con Web Component
Struttura file:
feedback-widget/
├── feedback-widget.js
└── index.html
Codice feedback-widget.js:
class FeedbackWidget extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
* { font-family: sans-serif; }
.widget { border: 1px solid #ccc; padding: 12px; border-radius: 8px; background: #f9f9f9; width: 250px; }
textarea, select, button { width: 100%; margin-top: 8px; }
.success { color: green; display: none; margin-top: 8px; }
</style>
<div class="widget">
<label for="rating">Valutazione:</label>
<select id="rating">
<option value="">--</option>
<option value="1">1 - Pessimo</option>
<option value="5">5 - Ottimo</option>
</select>
<textarea id="comment" placeholder="Scrivi un commento..."></textarea>
<button id="submit">Invia Feedback</button>
<div class="success">Grazie per il feedback!</div>
</div>
`;
}
connectedCallback() {
const rating = this.shadowRoot.getElementById('rating');
const comment = this.shadowRoot.getElementById('comment');
const button = this.shadowRoot.getElementById('submit');
const success = this.shadowRoot.querySelector('.success');
button.addEventListener('click', async () => {
const payload = {
rating: rating.value,
comment: comment.value,
};
await fetch('https://example.com/api/feedback', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
});
success.style.display = 'block';
button.disabled = true;
});
}
}
customElements.define('feedback-widget', FeedbackWidget);
HTML demo index.html:
<!DOCTYPE html>
<html lang="it">
<head>
<script type="module" src="./feedback-widget.js"></script>
</head>
<body>
<h2>Feedback Portale</h2>
<feedback-widget></feedback-widget>
</body>
</html>
✅ Vantaggi ottenuti
| Obiettivo | Risultato con Web Component |
|---|---|
| UI standard in tutte le app | ✅ Stile e struttura centralizzati |
| Integrazione facile | ✅ Tag HTML nativo |
| Incapsulamento logica/stile | ✅ Shadow DOM |
| Nessuna dipendenza da framework | ✅ Funziona ovunque |
| Manutenzione e versioning | ✅ Distribuibile via CDN/NPM |
📈 Estensioni possibili
-
Tema dinamico: <feedback-widget theme="dark">
-
Eventi custom: this.dispatchEvent(new CustomEvent('submitted'))
-
Configurabilità via attributi (endpoint, lang)
-
Test: unitari con Jest, e2e con Playwright
-
Distribuzione: via CDN (Skypack, jsDelivr) o npm
Tema dinamico: <feedback-widget theme="dark">
Eventi custom: this.dispatchEvent(new CustomEvent('submitted'))
Configurabilità via attributi (endpoint, lang)
Test: unitari con Jest, e2e con Playwright
Distribuzione: via CDN (Skypack, jsDelivr) o npm
📒 NOTA: Cos'è connectedCallback()?
È un metodo del ciclo di vita che viene chiamato automaticamente dal browser quando un Web Component viene inserito nel DOM (documento HTML).
📌 A cosa serve?
Serve per eseguire codice di inizializzazione, come:
- aggiungere event listener,
- fare fetch di dati,
- aggiornare l’interfaccia,
- inizializzare logica che richiede che il componente sia effettivamente "attivo" nella pagina.
📘 Sintassi base
class MyElement extends HTMLElement {
connectedCallback() {
console.log('Il componente è stato aggiunto al DOM!');
}
}
customElements.define('my-element', MyElement);
🔁 Quando viene chiamato?
- ✅ Appena il componente è inserito nel DOM, anche se dinamicamente
- ❌ Non viene chiamato finché l’elemento è creato in JS ma non ancora aggiunto al DOM
🧠 Esempio pratico
class HelloWorld extends HTMLElement {
connectedCallback() {
this.innerHTML = `<p>Ciao dal Web Component!</p>`;
}
}
customElements.define('hello-world', HelloWorld);
Nel DOM:
<hello-world></hello-world>
🔁 Quando il browser "vede" <hello-world>, invoca connectedCallback() e sostituisce il contenuto con quello definito.
🧹 Nota: ci sono anche altri metodi utili
| Metodo | Quando viene chiamato |
|---|---|
connectedCallback() |
Quando l’elemento entra nel DOM |
disconnectedCallback() |
Quando viene rimosso dal DOM |
attributeChangedCallback() |
Quando cambiano attributi osservati |
adoptedCallback() |
Quando viene spostato tra documenti (es. iframe) |
🧩 Conclusioni
I Web Components rappresentano un’alternativa robusta, standard e interoperabile per costruire UI moderne. Offrono:
- Isolamento perfetto di stile e logica
- Riutilizzo tra framework e app
- Semplicità d’integrazione in microfrontend o portali legacy
- Manutenzione e test centralizzati
Se il tuo team lavora in ambienti misti o su progetti enterprise, i Web Components possono fare la differenza tra un design system fragile e uno scalabile, solido e moderno.
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment
