Web Components Lifecycle Callbacks

Custom element lifecycle callbacks with timing and connectedCallback vs constructor

Reference for custom element lifecycle callbacks — constructor, connectedCallback, disconnectedCallback, adoptedCallback and attributeChangedCallback — with when each fires and what work belongs in each phase.

Why should I not access attributes in the constructor?

The spec forbids reading attributes, children or the parent in the constructor, because an element created via document.createElement is not yet upgraded and an element parsed from HTML may have its constructor run before its attributes and children are present. Do that work in connectedCallback instead, where the element is in the DOM.

Custom elements expose a small set of lifecycle callbacks that the browser invokes at well-defined moments. This reference lists each one, when it fires, and what work belongs there.

How it works

You define callbacks as methods on a class extending HTMLElement:

class MyEl extends HTMLElement {
  static get observedAttributes() { return ["value"]; }
  constructor() { super(); this.attachShadow({ mode: "open" }); }
  connectedCallback() { /* in the DOM — render, add listeners */ }
  disconnectedCallback() { /* removed — clean up listeners/timers */ }
  attributeChangedCallback(name, oldV, newV) { /* observed attr changed */ }
  adoptedCallback() { /* moved to a new document */ }
}
customElements.define("my-el", MyEl);

The ordering rule that trips people up: the constructor may run before the element’s attributes and children exist (or before it is even in a document), so it must not touch them. Anything that reads the DOM, attributes, or parent belongs in connectedCallback.

Tips and notes

  • connectedCallback / disconnectedCallback can fire multiple times as the node moves around the DOM. Make connect logic idempotent; always remove listeners and timers on disconnect.
  • attributeChangedCallback only fires for attributes named in the static observedAttributes array — and it fires once per such attribute already set at upgrade time.
  • Attach the shadow root in the constructor (it has no DOM dependency); render into it on connect.
  • Use adoptedCallback only when you move nodes between documents (rare).