loading image
Back to glossary

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

user-card.ts
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:

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

  1. Create an ES6 class extending HTMLElement or an existing HTML element (HTMLButtonElement, etc.)
  2. Define observed attributes with the static observedAttributes method if needed
  3. Implement the constructor to initialize state and attach Shadow DOM if required
  4. Code lifecycle callbacks (connectedCallback for initialization, disconnectedCallback for cleanup)
  5. Register the custom element with customElements.define('element-name', ElementClass)
  6. Check browser support and use a polyfill if necessary for older browsers
  7. Document the component API (attributes, properties, events, slots)
  8. 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.

Themoneyisalreadyonthetable.

In 1 hour, discover exactly how much you're losing and how to recover it.

Web development, automation & AI agency

contact@peaklab.fr
Newsletter

Get our tech and business tips delivered straight to your inbox.

Follow us

© PeakLab 2025