Isaac.

web

Web Components Basics

Create reusable custom HTML elements with Web Components.

By Emem IsaacFebruary 24, 20252 min read
#web components#custom elements#shadow dom#slots
Share:

A Simple Analogy

Web Components are like LEGO bricks. Self-contained, reusable pieces that work together without conflict.


Why Web Components?

  • Reusable: Use across projects
  • Encapsulation: Isolated styles/scripts
  • Framework-agnostic: Work with any framework
  • Standard: Native browser support
  • Composable: Build complex UIs

Custom Element

class MyCounter extends HTMLElement {
  constructor() {
    super();
    this.count = 0;
  }
  
  connectedCallback() {
    this.render();
    this.querySelector('button').addEventListener('click', () => {
      this.count++;
      this.render();
    });
  }
  
  render() {
    this.innerHTML = `
      <p>Count: ${this.count}</p>
      <button>Increment</button>
    `;
  }
}

customElements.define('my-counter', MyCounter);
<my-counter></my-counter>

Shadow DOM

class MyCard extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <style>
        .card { border: 1px solid #ccc; padding: 16px; }
        h2 { margin: 0; color: #333; }
      </style>
      <div class="card">
        <h2>My Card</h2>
        <slot></slot>
      </div>
    `;
  }
}

customElements.define('my-card', MyCard);
<my-card>
  <p>This content goes in the slot</p>
</my-card>

Slots (Content Projection)

class MyLayout extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <style>
        .container { display: flex; gap: 16px; }
        .header { background: #f0f0f0; padding: 16px; }
        .content { flex: 1; }
      </style>
      <div class="container">
        <div class="header">
          <slot name="header"></slot>
        </div>
        <div class="content">
          <slot></slot>
        </div>
      </div>
    `;
  }
}

customElements.define('my-layout', MyLayout);
<my-layout>
  <h1 slot="header">Welcome</h1>
  <p>Main content here</p>
</my-layout>

Attributes & Properties

class MyButton extends HTMLElement {
  get disabled() {
    return this.hasAttribute('disabled');
  }
  
  set disabled(value) {
    if (value) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }
  
  connectedCallback() {
    this.update();
  }
  
  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'disabled') {
      this.update();
    }
  }
  
  static get observedAttributes() {
    return ['disabled'];
  }
  
  update() {
    this.style.opacity = this.disabled ? '0.5' : '1';
  }
}

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

Best Practices

  1. Semantic: Use meaningful element names (dash required)
  2. Encapsulation: Use Shadow DOM for style isolation
  3. Documentation: Document attributes and slots
  4. Accessibility: Include ARIA labels
  5. Performance: Lazy load templates

Related Concepts

  • Custom Elements v1
  • Template element
  • Lit library (helpers)
  • Framework integration

Summary

Create reusable custom HTML elements with Shadow DOM for encapsulation and slots for flexible content projection.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours