Customized Built-ins
Two kinds of custom elements
Section titled “Two kinds of custom elements”The Web Components spec defines two flavors of custom element:
| Flavor | Extends | Registered as | Used in HTML |
|---|---|---|---|
| Autonomous | HTMLElement | customElements.define('x-btn', XBtn) | <x-btn> |
| Customized built-in | e.g. HTMLButtonElement | customElements.define('x-btn', XBtn, { extends: 'button' }) | <button is="x-btn"> |
Customized built-ins: syntax
Section titled “Customized built-ins: syntax”A customized built-in extends a specific HTML element class and declares which built-in it extends in the third argument to define:
class FancyButton extends HTMLButtonElement { connectedCallback() { this.style.background = '#4f46e5'; this.style.color = '#fff'; this.style.borderRadius = '6px'; this.style.padding = '.4rem 1rem'; this.style.border = 'none'; this.style.cursor = 'pointer'; }}
customElements.define('fancy-button', FancyButton, { extends: 'button' });In HTML, the is attribute activates the customized built-in on a standard element:
<button is="fancy-button">Click me</button>The element is still a <button> — it inherits all the built-in semantics (focusable, submits forms, responds to disabled) plus your custom behavior.
The support caveat
Section titled “The support caveat”Safari does not support customized built-ins and has explicitly declined to implement them. This is a significant portability problem: a <button is="fancy-button"> will render as a plain unstyled button in Safari.
There are polyfills (such as @ungap/custom-elements), but they add weight and complexity.
<!-- This renders correctly in Chrome/Firefox but NOT in Safari: --><button is="fancy-button">Safari renders this as a plain button</button>The portable choice: autonomous elements
Section titled “The portable choice: autonomous elements”Autonomous custom elements (extends HTMLElement) are fully supported everywhere. For the button example, an equivalent autonomous element:
class XButton extends HTMLElement { connectedCallback() { this.innerHTML = '<button style="background:#4f46e5;color:#fff;border-radius:6px;' + 'padding:.4rem 1rem;border:none;cursor:pointer">' + (this.textContent || 'Click me') + '</button>'; }}customElements.define('x-button', XButton);You lose the automatic inheritance of native <button> semantics (you must add role="button" and keyboard handling manually), but you gain full cross-browser support.
Runnable demo (autonomous element)
Section titled “Runnable demo (autonomous element)”The demo below uses an autonomous element — it works in every browser. The customized built-in syntax is shown for reference only.
Browser note: The customized built-in pattern (
class extends HTMLButtonElement+<button is="...">) is not supported in Safari. Use autonomous elements (class extends HTMLElement) for portable, cross-browser Web Components.