Native Lifecycle Callbacks for Custom Elements

AVAILABLE API VERSIONS
Available in LWC API v61.0 and later

In LWC API v61.0, LWC uses native browser APIs to call component lifecycle callbacks.

To support legacy browsers, LWC API v60.0 and earlier call these hooks using a synthetic polyfill. For backwards compatibility, these earlier versions of the LWC API use the synthetic polyfill in all browsers.

Native lifecycle callbacks align with current browser standards. They enable support for the ElementInternals API and the Form-Associated Custom Elements (FACE) API in LWC. Also, native callbacks fix common memory leaks caused by disconnectedCallback() firing inconsistently.

In LWC, native browser APIs affect how connectedCallback(), disconnectedCallback(), and sometimes renderedCallback() fire in your components. Beginning in LWC API v61.0, these hooks reflect the following changes.

Say you're firing the event onClick from connectedCallback():

connectedCallback() triggers when you insert element into the DOM. You can do this using appendChild().

In LWC v61.0 and later:

LWCs only fire connectedCallback() if they're connected to the DOM. Elements connected to the DOM are inside the document.

To ensure that connectedCallback() fires, you have to insert element into the DOM tree like this:

In LWC v60.0 and earlier:

connectedCallback() fires regardless of whether or not an element is connected to the DOM. For this example, only appending the element to the div still triggers this lifecycle hook.

Say connectedCallback() throws an error for some component c-app.

Since the error is fired by connectedCallback(), you can only it catch it when element gets inserted into the DOM.

In LWC API v61.0 and later:

When elements are added or removed from the DOM, connectedCallback() and disconnectedCallback() dispatch errors globally.

To catch the error on .appendChild(), you have to use a global error listener.

Events dispatched in connectedCallback() and disconnectedCallback() aren't impacted by this change.

If you're using @lwc/jest-*, you can call the toThrowInConnectedCallback() API to handle this logic for you. For more information, check out the @lwc/jest-preset usage guide.

In LWC API v60.0 and earlier:

connectedCallback() and disconnectedCallback() dispatch errors locally, so you can catch the error synchronously on .appendChild().

In native shadow only, the order in which these two hooks traverse the DOM tree depends on which version of LWC you're using.

Say you have this DOM tree:

Here's the order in which connectedCallback() fires in each component.

LWC v61.0 and LaterLWC v60.0 and Earlier
  1. c-a
  2. c-a-child
  3. c-b
  4. c-b-child
  5. c-c
  6. c-c-child
  1. c-a
  2. c-b
  3. c-c
  4. c-c-child
  5. c-b-child
  6. c-a-child

In the following DOM tree, the <c-parent> and <c-child> components are slotted. They aren't part of the shadow roots of their containers.

Here's how renderedCallback() fires in a different order depending on the LWC version being used.

LWC v61.0 and LaterLWC v60.0 and Earlier
  1. grandparent's connectedCallback()
  2. grandparent's renderedCallback()
  3. parent's connectedCallback()
  4. parent's renderedCallback()
  5. child's connectedCallback()
  6. child's renderedCallback()
  1. grandparent's connectedCallback()
  2. parent's connectedCallback()
  3. child's connectedCallback()
  4. child's renderedCallback()
  5. parent's renderedCallback()
  6. grandparent's renderedCallback()

Say you want to select element's child component c-child in shadow DOM. In this sample code, you also log c-child in the console.

In LWC API v61.0 and later:

To render <c-child>, you have to append the parent component element to the DOM.

For this example, append the div to the document's body. The following code logs the <c-child> component in the console.

If you don't append the div to the document, c-child doesn't render and the console logs null.

In LWC API v60.0 and earlier:

The child component c-child gets rendered regardless of whether or not the parent element is connected to the DOM. For this example, only appending element to the div still renders c-child and the console logs c-child.

You should put your setup logic in connectedCallback() and your teardown logic in disconnectedCallback(). If component data changes, rendereredCallback() may fire more than once. It also might not fire during list reordering.

Say you have the following for:each iteration.

The items array is originally [1, 2, 3, 4]. Later, items gets reordered to [3, 4, 2, 1].

In LWC API v61.0 and later:

When elements are removed and then re-inserted into the DOM, connectedCallback() and disconnectedCallback() fire for each list item component. If the data bindings inside the component change too, renderedCallback() also fires. renderedCallback() does not run for any of the component's children.

In this example, the LWC framework removes the items 1 and 2 and re-inserts them at the end of the list. After this reordering, disconnectedCallback() fires for 1 and 2. Then, connectedCallback() fires for both items.

In LWC API v60.0 and earlier:

During reordering, the connectedCallback(), disconnectedCallback(), and renderedCallback() lifecycle callbacks don't fire.

See Also