Build Reusable UI with Web Components: A Step‑by‑Step Counter Example

Learn how to create reusable, encapsulated UI components using Web Components by building a simple counter element with Custom Elements, Templates, Shadow DOM, properties, events, and attributes, and see how to integrate it into any framework, handle data binding, and dispatch custom events.

QQ Music Frontend Team
QQ Music Frontend Team
QQ Music Frontend Team
Build Reusable UI with Web Components: A Step‑by‑Step Counter Example

Custom Elements

Custom Elements API allows defining new HTML tags by extending HTMLElement. A minimal custom element can be created with a class that defines a constructor and connectedCallback, then registered with customElements.define.

class XCounter extends HTMLElement {
  constructor() {
    super();
    this._value = 0;
    this.attachShadow({mode: 'open'});
  }
  connectedCallback() {
    this.shadowRoot.innerHTML = `
      <style>button, p {display:inline-block;}</style>
      <button aria-label="decrement">-</button>
      <p>0</p>
      <button aria-label="increment">+</button>`;
    this._setup();
  }
  _setup() {
    this._valueElement = this.shadowRoot.querySelector('p');
    this._incBtn = this.shadowRoot.querySelectorAll('button')[1];
    this._decBtn = this.shadowRoot.querySelectorAll('button')[0];
    this._incBtn.addEventListener('click', () => this.value++);
    this._decBtn.addEventListener('click', () => this.value--);
  }
  set value(v) {
    this._value = v;
    this._valueElement.innerText = v;
    this.dispatchEvent(new CustomEvent('valueChange', {detail: v}));
  }
  get value() { return this._value; }
  static get observedAttributes() { return ['value']; }
  attributeChangedCallback(name, oldVal, newVal) {
    if (name === 'value') this.value = parseInt(newVal, 10);
  }
}
customElements.define('x-counter', XCounter);

Properties

The component exposes a public value property with getter and setter. Changing the property updates the displayed number and fires a valueChange custom event.

Events

Consumers can listen for the valueChange event to react when the counter changes.

const counter = document.querySelector('x-counter');
counter.addEventListener('valueChange', e => console.log('new value:', e.detail));
counter.value = 10;

Attributes

Initial values can be passed via the value attribute. The attribute is observed and synchronized with the property.

<x-counter value="5"></x-counter>

Summary

Web Components provide a standards‑based way to build reusable UI widgets that work across frameworks. By combining Custom Elements, Templates, and Shadow DOM you get encapsulation, style isolation, and a clean API for properties, events, and attributes.

Web Component example
Web Component example
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendJavaScriptweb componentsShadow DOMcustom elements
QQ Music Frontend Team
Written by

QQ Music Frontend Team

QQ Music Web Frontend Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.