Frontend Development 10 min read

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

.

<code>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);
</code>

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.

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

Attributes

Initial values can be passed via the

value

attribute. The attribute is observed and synchronized with the property.

<code>&lt;x-counter value="5"&gt;&lt;/x-counter&gt;
</code>

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
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

login 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.