Custom Elements
Web standard enabling creation of reusable HTML components with custom behavior and encapsulation, native to the browser.
Updated on January 25, 2026
Custom Elements are a web standard API enabling developers to create their own HTML tags with custom behavior. Part of the Web Components specification, they provide a native way to build reusable components without relying on third-party frameworks. This technology allows extending HTML vocabulary with elements that encapsulate functionality, style, and structure.
Custom Elements Fundamentals
- Native browser API based on ES6 JavaScript classes extending HTMLElement
- Registration via customElements.define() creating a binding between class and tag name
- Two types: Autonomous (standalone elements) and Customized Built-in (extensions of native elements)
- Lifecycle callbacks (connectedCallback, disconnectedCallback, attributeChangedCallback) for lifecycle management
- Native compatibility with Shadow DOM for style and markup encapsulation
Benefits of Custom Elements
- Maximum reusability: framework-independent components usable everywhere
- Native performance: direct browser execution without abstraction layer
- Enhanced maintainability: clear encapsulation of responsibilities and logic
- Complete interoperability: works with all modern frameworks (React, Vue, Angular)
- Guaranteed longevity: stable web standard supported by all modern browsers
- SEO-friendly: server-side rendering possible and valid HTML markup
Practical Example
class UserCard extends HTMLElement {
static get observedAttributes() {
return ['username', 'avatar', 'role'];
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (oldValue !== newValue) {
this.render();
}
}
render() {
const username = this.getAttribute('username') || 'Anonymous';
const avatar = this.getAttribute('avatar') || '/default-avatar.png';
const role = this.getAttribute('role') || 'User';
this.shadowRoot!.innerHTML = `
<style>
:host {
display: block;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
max-width: 300px;
}
.card-header {
display: flex;
align-items: center;
gap: 12px;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
}
.username {
font-weight: 600;
font-size: 1.1em;
margin: 0;
}
.role {
color: #666;
font-size: 0.9em;
}
</style>
<div class="card-header">
<img class="avatar" src="${avatar}" alt="${username}">
<div>
<h3 class="username">${username}</h3>
<p class="role">${role}</p>
</div>
</div>
`;
}
}
// Register the custom element
customElements.define('user-card', UserCard);Usage in HTML:
<user-card
username="Marie Dupont"
avatar="/avatars/marie.jpg"
role="Senior Developer">
</user-card>
<user-card
username="Jean Martin"
avatar="/avatars/jean.jpg"
role="Product Manager">
</user-card>Implementation Steps
- Create an ES6 class extending HTMLElement or an existing HTML element (HTMLButtonElement, etc.)
- Define observed attributes with the static observedAttributes method if needed
- Implement the constructor to initialize state and attach Shadow DOM if required
- Code lifecycle callbacks (connectedCallback for initialization, disconnectedCallback for cleanup)
- Register the custom element with customElements.define('element-name', ElementClass)
- Check browser support and use a polyfill if necessary for older browsers
- Document the component API (attributes, properties, events, slots)
- Test accessibility (ARIA attributes, keyboard navigation, screen readers)
Pro tip
Always follow the naming convention with at least one hyphen (e.g., user-card, custom-button) to avoid conflicts with current and future native HTML elements. Use TypeScript to type your custom elements and benefit from autocompletion. Consider exposing custom events for communication with the outside world rather than depending on callbacks passed as properties.
Tools and Ecosystem
- Lit: lightweight Google library for creating performant web components with reactive templates
- Stencil: compiler generating optimized web components with TypeScript and JSX
- FAST: Microsoft framework for building design systems based on web components
- Webcomponents.org: community registry of reusable custom elements
- Custom Elements Everywhere: compatibility tests with major frameworks
- @webcomponents/polyfills: official polyfills for legacy browser support
Custom Elements represent a strategic investment for any organization seeking to build a sustainable and independent frontend architecture. By relying on native web standards rather than ephemeral frameworks, they guarantee code longevity and reduce technical debt. Their interoperability enables progressive modernization of existing applications and creation of reusable design systems across different technologies, optimizing the return on investment of frontend development.
