Building Reusable UI with HTML5 Custom Elements: A Practical Guide
Learn how HTML5 Custom Elements let you create reusable, framework-free UI tags that encapsulate markup, style, and behavior, with a worked counter example and notes on style scoping and browser support.
20 Mar 2026, 05:22 UTC

Problem: Repetitive UI markup
When you need the same button, card, or form control across many pages, copying and pasting markup leads to duplicated code and makes updates error-prone. In lightweight projects that avoid heavy frameworks, this duplication can quickly become a maintenance burden.
Defining a Custom Element
HTML5 Custom Elements let you create a new tag that bundles its own markup, style, and behavior. You write a JavaScript class that extends HTMLElement, implement lifecycle callbacks such as connectedCallback to render the element’s initial content and attributeChangedCallback to react to attribute changes, then register the class with customElements.define.
Worked Example: A Counter
The following example defines a <my-counter> element that displays a number and increments when clicked. It reads an optional start attribute to set the initial value.
class MyCounter extends HTMLElement {
static get observedAttributes() {
return ['start'];
}
constructor() {
super();
this._count = 0;
}
connectedCallback() {
this._render();
this.addEventListener('click', this._onClick);
}
disconnectedCallback() {
this.removeEventListener('click', this._onClick);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'start' && newValue !== null) {
this._count = parseInt(newValue, 10) || 0;
this._render();
}
}
_onClick() {
this._count += 1;
this._render();
}
_render() {
this.textContent = this._count;
}
}
customElements.define('my-counter', MyCounter);
Using the Element
After the script runs, you can place the tag anywhere in your HTML:
<my-counter start='5'></my-counter>
The element will initialize with the value 5 and increase by one each time the user clicks it.
Trade‑offs and Limitations
- Without Shadow DOM, styles defined inside the element can leak out or be overridden by page CSS. A simple workaround is to prefix class names with the element’s name (e.g.,
my-counter-button) or adopt a naming convention like BEM. - All modern browsers support Custom Elements natively. For legacy browsers such as IE11 or early Edge, you need a polyfill like the
webcomponents.jslibrary. Adding the polyfill is as easy as including a script tag before your element definition. - Custom Elements only encapsulate the DOM structure; they do not provide built‑in state management or reactivity beyond what you implement yourself.
Actionable Next Steps
Start by extracting one repeated UI widget into a Custom Element. Define the class, register it, and replace the duplicated markup with the new tag. Verify the element works in your target browsers, add a polyfill if you need to support older versions, and consider using Shadow DOM if style isolation becomes a problem.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.