Frontend5 min read

Web Components

Build framework-agnostic UI with Web Components - Custom Elements, Shadow DOM encapsulation, HTML Templates, and the Lit library.

Web ComponentsCustom ElementsShadow DOMHTML TemplatesLit

skill: Web Components category: Frontend tags: [Web Components, Custom Elements, Shadow DOM, HTML Templates, Lit] readingTime: 5

Web Components are native browser primitives - they work in any framework (React, Vue, Angular) and without any framework. Shadow DOM provides true CSS encapsulation. Use raw Web Components for design system tokens shared across frameworks; use Lit for anything with reactive state.

Workflow

  1. Create a Custom ElementCustom Elements extend HTMLElement - the browser treats them like native elements. Lifecycle callbacks (connectedCallback, disconnectedCallback, attributeChangedCallback) manage element state.
  2. Use HTML templates and slotsHTML <template> elements aren't rendered until cloned - efficient for repeated patterns. Slots project external content into Shadow DOM.
  3. Build with Lit for reactive componentsLit is a lightweight library (5KB) built on Web Components - adds reactive properties, templates with html tagged literals, and efficient updates without a virtual DOM.
  4. Use Web Components in React and VueWeb Components are standard HTML - any framework that renders HTML can use them. React 19 added native Web Component support.
  5. Dispatch and handle Custom EventsCustom Elements communicate with parent components through custom DOM events - a standard, framework-agnostic API for component communication.

Output Format

Web Components: Custom Element with Shadow DOM, HTML templates and slots, Lit for reactive properties, React/Vue consumption, and CustomEvent with composed: true.

class MyEl extends HTMLElement {
  static observedAttributes = []
  connectedCallback() { this.attachShadow({mode:"open"}); this.#render(); }
  attributeChangedCallback() { this.#render(); }
}
customElements.define("my-el", MyEl);
Lit: @property, html`...`, @click=${handler}

Examples

Input

Output

Edge Cases & Gotchas

  • Custom element name requires hyphen - All custom element names must contain a hyphen (my-element, app-card) to distinguish them from future HTML elements. Single-word names throw a DOMException.
  • Shadow DOM and global CSS - CSS custom properties (variables) pierce Shadow DOM boundaries. Stylesheets from outside don't - this is the encapsulation feature, not a bug. Use CSS custom properties for theming.
  • Server-side rendering - Web Components are client-side only - they require JavaScript and the browser DOM. For SSR, use Declarative Shadow DOM (HTML attribute shadowrootmode="open") - supported in Chrome 111+.
  • Form association - Custom elements don't participate in form submission by default. Use the FormAssociated API (attachInternals(), setFormValue()) to make them work with <form> elements.

When to Break These Rules

  • Use React or Vue components within a single framework project - Web Components add boilerplate compared to framework components when you're not crossing framework boundaries.
  • Use design tokens (CSS custom properties) without Web Components when you just need shared styling - not all design system elements need to be components.

Reference Files

  • https://developer.mozilla.org/en-US/docs/Web/API/Web_components
  • https://lit.dev/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.