Understanding connectedCallback in Custom Elements

This article explains the purpose, timing, and practical implementation of the connectedCallback lifecycle method in JavaScript Web Components. You will learn how this method functions when an element is inserted into the Document Object Model (DOM), what tasks should be performed within it, how it compares to the constructor, and how to avoid common pitfalls when building custom elements.

What is connectedCallback?

The connectedCallback is one of the standard lifecycle hooks defined in the Custom Elements specification. It automatically fires each time a custom element is appended into a document-connected DOM tree.

Unlike the class constructor(), which only runs once when the element is instantiated in memory, connectedCallback executes whenever the browser attaches the node to the active document.

When Does It Trigger?

The browser triggers connectedCallback under several conditions: * When the HTML parser encounters the custom tag during initial page load. * When you programmatically append the element using methods like node.appendChild(), node.append(), or node.insertAdjacentElement(). * When an element is moved from one parent node to another within the live document.

Because an element can be detached and re-attached multiple times, connectedCallback can execute more than once during an element’s lifetime.

Typical Use Cases

The connectedCallback method is the ideal place to perform initialization tasks that require access to the DOM or parent document context:

  1. Rendering DOM Content: Populating the element’s innerHTML or attaching nodes to its Shadow DOM.
  2. Accessing Attributes and Children: Reading attributes set in HTML or querying child nodes, which are not guaranteed to be accessible inside the constructor.
  3. Setting Up Event Listeners: Attaching listeners to the window, document, or external services.
  4. Fetching Remote Data: Initiating API calls or network requests needed to render the component.
  5. Registering Observers: Initializing ResizeObserver, IntersectionObserver, or MutationObserver instances.

Basic Implementation Example

Below is an example of a simple custom element utilizing connectedCallback:

class UserBadge extends HTMLElement {
  constructor() {
    super();
    // Initialize private state and attach shadow root
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    // Access attributes and render UI
    const username = this.getAttribute('username') || 'Anonymous';
    this.shadowRoot.innerHTML = `
      <style>
        .badge { font-family: sans-serif; padding: 4px 8px; background: #eee; border-radius: 4px; }
      </style>
      <span class="badge">User: ${username}</span>
    `;

    // Add global listener
    this.handleResize = () => console.log('Window resized');
    window.addEventListener('resize', this.handleResize);
  }

  disconnectedCallback() {
    // Clean up to prevent memory leaks
    window.removeEventListener('resize', this.handleResize);
  }
}

customElements.define('user-badge', UserBadge);

constructor vs. connectedCallback

Understanding the separation of responsibilities between the constructor and connectedCallback is critical:

Feature constructor() connectedCallback()
Execution Runs once when instantiated Runs every time the element is attached to the DOM
DOM Availability Attributes and child nodes are unavailable Full DOM context and attributes are available
Best Used For Initializing state, creating Shadow Root Fetching data, rendering UI, binding global events

Important Considerations