No history yet

Introduction to Web Components

Your Own HTML Building Blocks

Imagine building a complex website with simple, reusable pieces, much like building a castle with LEGO bricks. Instead of copying and pasting the same block of HTML, CSS, and JavaScript for a user profile card or a special button, you could just use a single, custom tag like <user-profile>.

This is the core idea behind Web Components. They are a set of technologies that let you create new, fully-featured HTML tags. These custom elements are encapsulated, meaning their internal workings and styling don't accidentally spill out and affect the rest of your page. They are also reusable, so you can build them once and use them anywhere you need them.

Web components work like Lego for HTML.

Web Components are built on four main technologies that work together. Let's break them down.

Custom Elements

The Custom Elements API is what allows you to define your own HTML tags. This is the heart of Web Components. You can create an element like <special-button> or <weather-widget> and teach the browser how to render it and what it should do.

To do this, you define a JavaScript class that extends the base HTMLElement class. Inside this class, you can define the element's behavior, methods, and lifecycle callbacks (special functions that run when the element is added to or removed from the page). Then, you register your new element with the browser, giving it a custom tag name.

class MyElement extends HTMLElement {
  constructor() {
    super();
    // ...element initialization...
  }
}

// Register the new element with the browser
customElements.define('my-element', MyElement);

Once registered, you can use <my-element> in your HTML just like any standard tag, such as <div> or <p>.

Shadow DOM

Shadow DOM is the key to encapsulation. It lets you attach a hidden, separate DOM tree to an element. This “shadow” tree is rendered separately from the main document’s DOM.

What does this mean in practice? It means the styles and scripts inside your component won't leak out and affect the rest of the page. Likewise, styles from the main page won't accidentally break your component's appearance. It creates a safe, self-contained bubble for your element's internal structure.

Think of it like a component with its own private room. The furniture and decorations (CSS) inside the room don't change the hallway, and the hallway's paint color doesn't seep into the room.

class MyElement extends HTMLElement {
  constructor() {
    super();
    // Create a shadow root
    const shadow = this.attachShadow({mode: 'open'});

    // Create elements to append to the shadow root
    const wrapper = document.createElement('span');
    wrapper.setAttribute('class', 'wrapper');
    wrapper.textContent = 'I am inside the Shadow DOM!';

    // Add styles that only apply within this component
    const style = document.createElement('style');
    style.textContent = `
      .wrapper {
        color: red;
      }
    `;

    shadow.appendChild(style);
    shadow.appendChild(wrapper);
  }
}

customElements.define('my-element', MyElement);

In this example, the red color style will only apply to the <span> inside this component, even if there are other spans on the page. The class="wrapper" also won't conflict with any other class of the same name outside the component.

HTML Templates and Imports

Defining a component's entire structure in JavaScript can be cumbersome. That's where the <template> element comes in.

An HTML template allows you to declare a chunk of markup that isn't rendered immediately when the page loads. Instead, it sits inert until you grab it with JavaScript and use it. This is perfect for defining the internal structure of a custom element. You write your HTML inside a <template> tag, and then your component's code can clone that content and attach it to its Shadow DOM.

<template id="my-element-template">
  <style>
    p {
      color: blue;
      font-weight: bold;
    }
  </style>
  <p>My custom element's content!</p>
</template>

<script>
  class MyElement extends HTMLElement {
    constructor() {
      super();
      const template = document.getElementById('my-element-template');
      const templateContent = template.content;

      this.attachShadow({mode: 'open'})
        .appendChild(templateContent.cloneNode(true));
    }
  }
  customElements.define('my-element', MyElement);
</script>

Finally, there are HTML Imports. The original idea was to use a <link> tag to import an HTML document containing components, much like you import a CSS file. However, this approach is now considered obsolete.

The modern web uses ES Modules instead. This allows you to define your custom element in a separate JavaScript file (.js) and then import it into your main application script. This is a more powerful and flexible way to organize and reuse your code.

Quiz Questions 1/5

What is the primary purpose of the Shadow DOM in Web Components?

Quiz Questions 2/5

To create a new custom element, you must define a JavaScript class that extends which base class?