Lightning Web Components (LWC) is Salesforce’s modern framework for building custom user interfaces in Salesforce applications. Understanding the component lifecycle is crucial for efficient component development, optimizing performance, and implementing complex functionalities.
Lifecycle hooks are special methods that get executed at specific points during a component’s existence. They allow developers to run custom code at key moments such as when a component is created, rendered, updated, or removed from the DOM. By leveraging these hooks strategically, you can control component behavior throughout its lifecycle.
A Lightning Web Component goes through the following phases:

Let’s explore each lifecycle hook in detail.
When it runs: At component creation, before any other lifecycle hook.
Use cases:
Do not:
Example:
import { LightningElement } from 'lwc';
export default class LifecycleDemo extends LightningElement {
constructor() {
super(); // Always call super() first
// Initialize properties
this.counter = 0;
this.initialized = true;
// Bind methods to maintain 'this' context
this.handleClick = this.handleClick.bind(this);
console.log('Constructor executed');
}
}
When it runs: When a component is inserted into the DOM.
Use cases:
Example:
import { LightningElement } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class LifecycleDemo extends LightningElement {
contacts;
error;
connectedCallback() {
console.log('Component connected to DOM');
// Fetch data when component is inserted into DOM
getContacts()
.then(result => {
this.contacts = result;
this.error = undefined;
})
.catch(error => {
this.error = error;
this.contacts = undefined;
});
// Set up interval
this.intervalId = setInterval(() => {
this.counter++;
}, 1000);
// Subscribe to platform events
this.subscription = subscribe(
this.channelName,
-1,
this.handleEvent.bind(this)
);
}
}
When it runs: After every render of the component. It runs after the first render and after every re-render caused by state changes.
Use cases:
Example:
import { LightningElement } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import chartjs from '@salesforce/resourceUrl/chartjs';
export default class ChartComponent extends LightningElement {
chart;
chartInitialized = false;
renderedCallback() {
console.log('Component rendered/re-rendered');
// Only initialize the chart library once
if (this.chartInitialized) {
return;
}
this.chartInitialized = true;
// Load the ChartJS library
loadScript(this, chartjs)
.then(() => {
// Get the canvas element
const canvas = this.template.querySelector('canvas');
// Initialize chart
this.chart = new Chart(canvas, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [{
label: 'Colors',
data: [12, 19, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)'
],
borderWidth: 1
}]
}
});
})
.catch(error => {
console.error('Error loading ChartJS', error);
});
}
}
When it runs: When a component is removed from the DOM.
Use cases:
Example:
import { LightningElement } from 'lwc';
import { unsubscribe } from 'lightning/empApi';
export default class LifecycleDemo extends LightningElement {
subscription;
intervalId;
connectedCallback() {
// Set up interval
this.intervalId = setInterval(() => {
console.log('Interval running');
}, 1000);
// Subscribe to platform events
this.subscription = subscribe(
this.channelName,
-1,
this.handleEvent.bind(this)
);
}
disconnectedCallback() {
console.log('Component removed from DOM');
// Clear interval
if (this.intervalId) {
clearInterval(this.intervalId);
}
// Unsubscribe from events
if (this.subscription) {
unsubscribe(this.subscription);
}
// Clean up any other resources
if (this.chart) {
this.chart.destroy();
}
}
}
errorCallback(error, stack)
error: The JavaScript error objectstack: The stack trace stringThe errorCallback does not catch errors that occur:
errorCallback is definedrender() methodExample:
<!-- parentComponent.html -->
<template>
<div class="parent-container">
<h2>Parent Component</h2>
<!-- Conditionally display error message -->
<template if:true={showError}>
<div class="error-message">
<lightning-icon icon-name="utility:error" alternative-text="Error" variant="error"></lightning-icon>
<span>{errorMessage}</span>
</div>
</template>
<!-- Child component that might throw an error -->
<c-error-prone-child></c-error-prone-child>
<div class="container">
<p>The parent component can catch and handle errors from the child component above.</p>
</div>
</div>
</template>
// parentComponent.js
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
// errorCallback lifecycle hook
// This runs when a child component throws an error
errorCallback(error, stack) {
// Handle error from child component
console.error('Error in child component:', error.message);
console.log('Stack trace:', stack);
// Implement custom error handling
// For example, display a friendly message to the user
this.errorMessage = 'Something went wrong in a child component. Please try again later.';
// You could also report the error to a logging service
// this.logErrorToService(error, stack);
// Return true to indicate that you've handled the error
return true;
}
// Example method to log errors to a service
logErrorToService(error, stack) {
// Implementation for error logging
}
}
/* parentComponent.css */
.parent-container {
padding: 1rem;
background-color: #f3f3f3;
border-radius: 4px;
}
.error-message {
background-color: #ffdde1;
color: #c23934;
padding: 0.5rem;
border-radius: 4px;
margin: 1rem 0;
display: flex;
align-items: center;
}
.error-message lightning-icon {
margin-right: 0.5rem;
}
.container {
margin-top: 1rem;
}
// errorProneChild.js
import { LightningElement, api } from 'lwc';
export default class ErrorProneChild extends LightningElement {
@api
get throwError() {
return this._throwError;
}
set throwError(value) {
this._throwError = value;
if (value === true) {
this.causeError();
}
}
_throwError = false;
connectedCallback() {
// This will intentionally cause an error after the component is connected
// In a real scenario, errors might happen due to data issues, unexpected state, etc.
setTimeout(() => {
this.causeError();
}, 2000);
}
causeError() {
// This will cause an error that will be caught by the parent's errorCallback
const nonExistentObject = undefined;
try {
// This will throw a TypeError
nonExistentObject.someProperty = 'This will cause an error';
} catch (error) {
// Throwing the error from here will trigger the parent's errorCallback
throw new Error('Child component failed: ' + error.message);
}
}
handleClick() {
// Another way to trigger an error
this.causeError();
}
}
<!-- errorProneChild.html -->
<template>
<div class="child-container">
<h3>Error-Prone Child Component</h3>
<p>This component will throw an error shortly after loading, which the parent will catch.</p>
<lightning-button
label="Force Error"
variant="destructive"
onclick={handleClick}>
</lightning-button>
</div>
</template>
/* errorProneChild.css */
.child-container {
padding: 1rem;
background-color: white;
border: 1px solid #d8dde6;
border-radius: 4px;
margin-top: 1rem;
}
When it runs: Before each time a component renders or re-renders.
Use cases:
Example:
import { LightningElement } from 'lwc';
import desktopTemplate from './desktopView.html';
import mobileTemplate from './mobileView.html';
import tabletTemplate from './tabletView.html';
export default class ResponsiveComponent extends LightningElement {
formFactor = 'DESKTOP';
// Determine device type on component initialization
connectedCallback() {
// This is simplified - you'd use more robust detection in a real app
const width = window.innerWidth;
if (width < 768) {
this.formFactor = 'PHONE';
} else if (width < 1024) {
this.formFactor = 'TABLET';
} else {
this.formFactor = 'DESKTOP';
}
}
// Dynamically return the appropriate template
render() {
console.log('Render method called, form factor:', this.formFactor);
switch(this.formFactor) {
case 'PHONE':
return mobileTemplate;
case 'TABLET':
return tabletTemplate;
default:
return desktopTemplate;
}
}
}
For a typical component, lifecycle hooks are executed in this order:
constructor()render() (initial)connectedCallback()renderedCallback() (first render)When a component’s state changes:
render()renderedCallback()When a component is removed:
disconnectedCallback()A common issue is creating infinite rendering loops. This happens when you modify reactive properties in renderedCallback() without proper guards.
Problem:
renderedCallback() {
// This creates an infinite loop!
this.counter++;
}
Solution:
renderedCallback() {
if (this.firstRender) {
this.firstRender = false;
this.counter++;
}
}
Problem:
connectedCallback() {
// This will fail - DOM isn't ready yet
const div = this.template.querySelector('div');
div.focus();
}
Solution:
renderedCallback() {
// Now the DOM is ready
const div = this.template.querySelector('div');
div.focus();
}
// Parent component
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
childReady = false;
handleChildReady() {
this.childReady = true;
console.log('Child component is ready');
}
}
// Child component
import { LightningElement } from 'lwc';
export default class ChildComponent extends LightningElement {
connectedCallback() {
// Notify parent that child is ready
setTimeout(() => {
const readyEvent = new CustomEvent('childready');
this.dispatchEvent(readyEvent);
}, 0);
}
}
import { LightningElement, track } from 'lwc';
export default class ConditionalComponent extends LightningElement {
@track showComponent = false;
toggleComponent() {
this.showComponent = !this.showComponent;
}
// This will run whenever component is shown or hidden
renderedCallback() {
console.log('Component render state changed:', this.showComponent ? 'showing' : 'hidden');
}
}
Understanding and effectively using Lightning Web Component lifecycle hooks is essential for building robust, performant Salesforce applications. By applying the right hook at the right time, you can create components that initialize properly, interact seamlessly with external systems, clean up after themselves, and handle errors gracefully.
Remember the key principles:
[…] Salesforce LWC Lifecycle Hooks Explained […]