Skip to content

Web Components Introduction

This lesson introduces Web Components as a set of browser-native standards for building reusable, encapsulated custom HTML elements that work across frameworks and plain HTML pages.

What Are Web Components?

Web Components are a collection of web platform APIs that let you create new HTML tags with their own structure, styling, and behavior. Instead of treating every UI piece as framework-specific code, you define a component once and use it like any other HTML element.

The term Web Components usually refers to three complementary standards that work together: Custom Elements, Shadow DOM, and HTML Templates. Modern libraries such as Lit build on top of these standards rather than replacing them.

Standard Purpose
Custom Elements Define new HTML tags and lifecycle behavior
Shadow DOM Encapsulate internal markup and styles
HTML Templates Store reusable markup with <template>
Slots Project external content into a component
Custom Events Communicate from child components to parents

Your First Web Component

A Web Component starts as a JavaScript class that extends HTMLElement. You register it with customElements.define(), then use it in HTML like any native tag.

class GreetingCard extends HTMLElement {
  connectedCallback() {
    const name = this.getAttribute("name") || "Guest";

    this.innerHTML = `
      <article class="card">
        <h2>Welcome, ${name}!</h2>
        <p>This is a browser-native custom element.</p>
      </article>
    `;
  }
}

customElements.define("greeting-card", GreetingCard);
<greeting-card name="Alex"></greeting-card>

After registration, <greeting-card> behaves like a first-class HTML element. Frameworks can render it, and plain pages can include it with a script tag.

How Web Components Work

A typical Web Component combines several building blocks. The class defines behavior, Shadow DOM isolates internal UI, templates keep markup reusable, and attributes or properties form the public API.

class ProfileBadge extends HTMLElement {
  constructor() {
    super();
    const template = document.getElementById("profile-badge-template");
    this.attachShadow({ mode: "open" });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  static get observedAttributes() {
    return ["status"];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === "status" && oldValue !== newValue) {
      this.shadowRoot.querySelector(".status").textContent = newValue;
    }
  }
}

This pattern separates the public tag from private implementation details. Consumers interact with attributes and events; the component manages its own DOM and styles internally.

The Three Core Standards

Standard What It Solves Example
Custom Elements Reusable tags with lifecycle hooks <date-picker>
Shadow DOM Scoped CSS and hidden internals attachShadow()
HTML Templates Inactive markup for cloning <template>

You do not have to use all three on day one. Many teams start with Custom Elements alone, then add Shadow DOM when styling isolation becomes important.

Web Components vs Framework Components

Topic Web Components Framework Components
Runtime Browser-native APIs Framework runtime required
Portability Works across frameworks Tied to one ecosystem
Encapsulation Shadow DOM built in Depends on framework patterns
Learning curve Lower-level browser APIs Higher-level abstractions
Best fit Shared UI, design systems App-specific views and state

Web Components are not a replacement for React, Vue, or Angular. They complement them. Many production apps use framework components for application screens and Web Components for shared primitives such as buttons, modals, and data tables.

Shadow DOM at a Glance

class StyledButton extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.innerHTML = `
      <style>
        button {
          padding: 0.5rem 1rem;
          border-radius: 0.5rem;
        }
      </style>
      <button><slot></slot></button>
    `;
  }
}

customElements.define("styled-button", StyledButton);
<styled-button>Save changes</styled-button>

Shadow DOM keeps internal styles from leaking out and external styles from breaking the component. Slots let users pass content into predefined places inside the shadow tree.

Where Web Components Are Used

  • Design systems shared across multiple apps and teams.
  • Embeddable widgets for third-party sites.
  • Micro frontends with framework-agnostic UI pieces.
  • CMS and static sites that need interactive components.
  • Browser extensions and progressive web apps.
  • Long-lived products that outlive a single framework choice.

Browser Support

Custom Elements, Shadow DOM, and HTML Templates are supported in all modern evergreen browsers. For older browsers, polyfills such as @webcomponents/webcomponentsjs can fill gaps, though most new projects target current browser versions.

Feature Support
Custom Elements Chrome, Firefox, Safari, Edge
Shadow DOM Chrome, Firefox, Safari, Edge
Declarative Shadow DOM Growing SSR support in modern browsers
Lit and other libraries Built on top of native APIs

What You Will Learn in This Tutorial

This Web Components tutorial series walks from fundamentals to production patterns. Here is the learning path ahead:

  • Basics — custom elements, templates, and slots.
  • Shadow DOM — encapsulation, styling, and events.
  • Lifecycle — connect, disconnect, and attribute callbacks.
  • APIs — properties, observed attributes, and custom events.
  • Lit — a popular library for building Web Components faster.
  • Production — testing, accessibility, performance, and SSR.

Building with Lit (Preview)

Many teams use Lit to reduce boilerplate. Lit still produces real Web Components under the hood.

import { LitElement, html, css } from "lit";

class CounterBadge extends LitElement {
  static properties = {
    count: { type: Number }
  };

  static styles = css`
    span {
      font-weight: bold;
    }
  `;

  render() {
    return html`<span>${this.count}</span>`;
  }
}

customElements.define("counter-badge", CounterBadge);

You will learn Lit later in this series. Starting with native APIs first helps you understand what Lit abstracts away.

When to Use Web Components

  • You need UI shared across multiple frameworks or teams.
  • You want encapsulation without a heavy runtime dependency.
  • You are building a design system or component library.
  • You need embeddable widgets for external sites.
  • You want standards-based components with long-term portability.
  • You are combining micro frontends with different tech stacks.

When to Choose Something Else

  • Your entire app lives in one framework with no sharing needs.
  • You need advanced state management built into the component layer.
  • Your team prefers framework-specific developer tooling only.
  • You are building a small prototype where speed matters more than reuse.

In those cases, framework components may be simpler. Web Components shine when reuse, encapsulation, and interoperability matter.

Getting Started Best Practices

  • Start with a small custom element before adding Shadow DOM.
  • Use hyphenated tag names such as app-header.
  • Keep the public API small: a few attributes and events.
  • Think about accessibility from the first component you build.
  • Document attributes, properties, slots, and emitted events.
  • Test components in plain HTML before integrating with a framework.
  • Prefer progressive enhancement over framework-only assumptions.

Common Beginner Mistakes

  • Assuming Web Components replace frameworks entirely.
  • Using tag names without a hyphen.
  • Doing too much DOM work in the constructor.
  • Skipping cleanup when elements are removed from the page.
  • Overusing Shadow DOM without a clear styling strategy.
  • Ignoring keyboard support and ARIA for custom widgets.
  • Building monolithic components instead of small focused ones.

Key Takeaways

  • Web Components are browser-native standards, not a framework.
  • Custom Elements, Shadow DOM, and Templates work together.
  • You create reusable tags with JavaScript classes.
  • Components work in plain HTML and inside React, Vue, and Angular.
  • Lit and similar libraries build on the same underlying APIs.
  • This tutorial series will take you from basics to production use.

Pro Tip

Build your first component in plain HTML and JavaScript before adding Lit or a framework wrapper. Understanding the native APIs makes every abstraction easier to debug later.