← Blog
Dev Web Components Lit Frontend JavaScript

Web Components: perché Lit è il framework giusto

I Web Components esistono da oltre dieci anni. Sono uno standard del browser, supportati nativamente da tutti i motori moderni, senza dipendenze esterne. Eppure non li usa quasi nessuno.

Il motivo non è tecnico. È che scriverli a mano è verboso al punto da essere scoraggiante. Poi ho scoperto Lit, e ho cambiato idea.

Il problema con i Web Components nativi

Un Web Component nativo è una classe JavaScript che estende HTMLElement. La API è potente ma cerimoniale:

class MyButton extends HTMLElement {
  static observedAttributes = ["label"];

  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback() {
    this.render();
  }

  render() {
    this.shadowRoot.innerHTML = `
      <button>${this.getAttribute("label")}</button>
    `;
  }
}

customElements.define("my-button", MyButton);

Questo è un bottone. Con una prop. Nessuna reattività reale, nessun sistema di template, nessuna gestione dello stato — tutto da implementare a mano ogni volta.

Nessuno scrive così in produzione. Ed è per questo che i Web Components non hanno preso piede.

Cos’è Lit

Lit è una libreria Google (open source, ~6KB gzipped) che aggiunge esattamente quello che manca ai Web Components nativi: un sistema di template reattivo, gestione delle proprietà, lifecycle hooks chiari.

Non è un framework alternativo — compila su Web Components standard. Quello che produce è un custom element registrabile con customElements.define, usabile in qualsiasi HTML, con qualsiasi framework o senza nessuno.

Lo stesso bottone di prima, in Lit:

import { LitElement, html, css } from "lit";
import { customElement, property } from "lit/decorators.js";

@customElement("my-button")
class MyButton extends LitElement {
  @property() label = "";

  static styles = css`
    button { padding: 0.5rem 1rem; }
  `;

  render() {
    return html`<button>${this.label}</button>`;
  }
}

Quando label cambia, Lit aggiorna solo la parte del DOM che dipende da quella proprietà — nessun re-render completo, nessun virtual DOM.

Perché Lit è la scelta giusta

1. Zero dipendenze a runtime

React ha bisogno di React. Vue ha bisogno di Vue. Un componente Lit compilato è un Web Component standard — il browser lo esegue senza nessuna libreria da caricare. Il bundle è quello della tua applicazione, non della tua applicazione più un framework.

Per design system distribuiti, widget da embeddare in siti di terzi, componenti che devono girare ovunque: è un vantaggio strutturale.

2. Funziona con qualsiasi stack

Un componente Lit si usa come un tag HTML qualsiasi:

<!-- In un file HTML statico -->
<my-button label="Invia"></my-button>

<!-- In React -->
<MyButton label="Invia" />

<!-- In Astro -->
<my-button label="Invia" />

<!-- In Vue -->
<my-button label="Invia" />

Questo è il vero vantaggio dei Web Components — e Lit lo rende praticabile. Puoi costruire un design system una volta sola e usarlo in qualsiasi progetto, indipendentemente dal framework che cambia ogni due anni.

3. Shadow DOM: stili incapsulati per davvero

Il Shadow DOM non è solo un dettaglio implementativo — è isolamento reale. Gli stili definiti dentro un componente Lit non escono fuori, e gli stili globali non entrano dentro (a meno che non li lasci passare con CSS custom properties).

static styles = css`
  /* Questi stili esistono solo dentro questo componente */
  :host {
    display: block;
  }
  button {
    background: var(--button-bg, #0070f3);
    color: white;
  }
`;

Nessun conflitto di classi, nessun BEM, nessun CSS Modules. L’incapsulamento è garantito dalla piattaforma.

4. Reattività granulare senza magia

Lit tiene traccia di quali parti del template usano quale proprietà. Quando una proprietà cambia, aggiorna solo quella porzione del DOM — non ri-renderizza tutto.

@customElement("user-card")
class UserCard extends LitElement {
  @property() name = "";
  @property() online = false;

  render() {
    return html`
      <div class="card">
        <span class="name">${this.name}</span>
        <span class="status">${this.online ? "online" : "offline"}</span>
      </div>
    `;
  }
}

Se cambia solo online, Lit aggiorna solo lo span.status. Semplice, prevedibile, senza virtual DOM overhead.

Quando usare Lit

Lit non è la risposta a tutto. Per applicazioni SPA complesse con routing, state management globale e server-side rendering, React o SvelteKit rimangono scelte più mature.

Dove Lit eccelle:

  • Design system condivisi tra più stack o team
  • Widget da embeddare in contesti eterogenei (CMS, siti terzi)
  • Componenti autonomi che devono durare nel tempo senza legarsi a un framework specifico
  • Progressive enhancement su siti con HTML esistente

Conclusione

I Web Components non hanno sfondato perché la DX nativa è povera. Lit risolve esattamente quel problema — aggiunge reattività, template expressions, CSS scoping e TypeScript support mantenendo l’output 100% standard.

Non è un compromesso. È Web Components come avrebbero dovuto essere fin dall’inizio.