connectedCallback(), renderedCallback(), and disconnectedCallback()), you can initialize properties, fetch server data, manipulate the DOM, and clean up listeners at the right time.
Building high-performance Salesforce applications requires an understanding of how components initialize, render, and unmount. Because Lightning Web Components (LWC) adhere strictly to modern Custom Elements standards, its lifecycle engine ensures predictable execution and efficient DOM updates. Below is a comprehensive breakdown of each lifecycle hook in its exact order of execution.
1. The Sequential Order of LWC Lifecycle Hooks
When a component loads on a Lightning page, its lifecycle methods execute in a strict chronological sequence:
- 1.
constructor(): Fires first when the component instance is created in memory. Flow flows from parent to child. - 2.
connectedCallback(): Fires when the component is inserted into the document DOM. Flow flows from parent to child. - 3.
render()(Optional): Overrides the default HTML template to conditionally render custom markup files. - 4.
renderedCallback(): Fires after the component template and all child components finish rendering. Flow flows from child to parent. - 5.
disconnectedCallback(): Fires when the component is removed from the DOM hierarchy. Flow flows from parent to child. - 6.
errorCallback(error, stack): Acts as an error boundary to capture unhandled exceptions thrown by child components.
- Creation (
constructor): Callsuper(); cannot access public@apiproperties or child DOM nodes. - Insertion (
connectedCallback): Best place for state initialization, LMS subscriptions, and imperative Apex calls. - Post-Render (
renderedCallback): Inspect rendered DOM elements; must guard property updates to prevent infinite loops. - Cleanup (
disconnectedCallback): Unsubscribe from message channels, window listeners, and clear intervals.
2. Deep Dive: Implementing Each Lifecycle Hook
constructor()Always call
super() first. Use this hook only for default variable setup.
import { LightningElement, api } from 'lwc';
export default class LifecycleDemo extends LightningElement {
@api recordId;
isInitialized = false;
constructor() {
super();
// Set default internal state
this.isInitialized = true;
console.log('1. constructor: Component instance created in memory');
// NOTE: this.recordId and this.template.querySelector() are NOT available yet!
}
}
connectedCallback()Use this hook to establish communication channels, subscribe to message channels, and load data.
connectedCallback() {
console.log('2. connectedCallback: Inserted into DOM. Record ID:', this.recordId);
// Perfect place to subscribe to Lightning Message Service (LMS)
// or trigger initial Apex queries
}
renderedCallback()Executes after every render and re-render pass. Always guard execution with a boolean flag.
hasRendered = false;
renderedCallback() {
console.log('3. renderedCallback: Template and children rendered');
// Guard flag ensures expensive DOM work or third-party JS only runs once
if (!this.hasRendered) {
this.hasRendered = true;
const container = this.template.querySelector('.content-wrapper');
if (container) {
console.log('DOM node safely queried:', container);
}
}
}
disconnectedCallback()Fires when the component is unmounted or destroyed. Clean up all resources to prevent memory leaks.
disconnectedCallback() {
console.log('4. disconnectedCallback: Component removed from DOM');
// Unsubscribe from LMS channels, remove window event listeners, and clear intervals
}
errorCallback()Captures unhandled errors thrown inside child lifecycle hooks or event handlers.
errorCallback(error, stack) {
console.error('Error caught by lifecycle error boundary:', error);
console.error('Stack trace:', stack);
// Render fallback UI or log error to an external monitoring service
}
3. Parent vs. Child Lifecycle Execution Flow
Understanding the difference between parent and child execution phases prevents race conditions and data synchronization bugs:
| Execution Phase | Direction | Execution Sequence |
|---|---|---|
| Instantiation | Parent → Child | Parent constructor() → Child constructor() |
| DOM Attachment | Parent → Child | Parent connectedCallback() → Child connectedCallback() |
| Rendering | Child → Parent | Child renderedCallback() → Parent renderedCallback() |
| Unmounting | Parent → Child | Parent disconnectedCallback() → Child disconnectedCallback() |
4. Common Traps & Lifecycle Best Practices
Updating any reactive property or tracked variable inside
renderedCallback() triggers a re-render pass, which immediately re-invokes renderedCallback(). This results in an infinite rendering loop that freezes the browser. Never update reactive properties inside renderedCallback() without a strict boolean execution guard.
In the
constructor(), the component has not yet been mounted to the DOM. Calling this.template.querySelector() will always return null, and accessing @api public properties passed from parent components will return undefined. Perform all initialization requiring public properties in connectedCallback().
- Always Unsubscribe: Every subscription created in
connectedCallback()must have a corresponding unsubscribe routine indisconnectedCallback()to prevent zombie listeners and memory leaks. - Use Standard Directives: Avoid manually rendering HTML strings via dynamic
render()overrides. Use template conditional directives (lwc:if,lwc:elseif,lwc:else) for clean, declarative markup management.
Summary
Mastering the lifecycle hooks in Lightning Web Components allows Salesforce developers to control precisely when components allocate resources, interact with the DOM, and update state. Following standard lifecycle rules ensures that your Salesforce user interfaces remain fast, memory-efficient, and easy to maintain across enterprise deployments.