Introduction
Lazy loading is an important performance technique in Lightning Web Components (LWC). In short, lazy loading delays loading of non-essential resources (components, scripts, data, or images) until they are actually needed. This reduces initial load time, improves perceived performance, and reduces network and CPU usage in Salesforce apps.
What is lazy loading in LWC?
Lazy loading in LWC means deferring the creation or fetching of components, external libraries, or data until the user interacts with the UI or the resource comes into view. Instead of loading everything at once on page load, you load only what’s necessary immediately and postpone the rest.
Why use lazy loading?
Benefits include:
- Faster initial rendering and improved Time to Interactive (TTI).
- Lower bandwidth and CPU usage for users who don’t access every feature.
- Better user experience for mobile networks or bandwidth-constrained environments.
Common lazy-loading patterns in LWC
1) Conditional rendering (template if:true)
Render child components or markup only when needed. Typically combined with an event or user action.
// parentComponent.js
import { LightningElement, track } from 'lwc';
export default class ParentComponent extends LightningElement {
@track showChild = false;
handleShow() {
this.showChild = true; // child is rendered only now
}
}
// parentComponent.html
2) Dynamic import for modules and components
Use JavaScript dynamic import() to load modules or helper code only when required. For heavy child components, you can dynamically import the module so the browser fetches the component bundle on demand.
// lazyLoader.js (parent)
import { LightningElement, track } from 'lwc';
export default class LazyLoader extends LightningElement {
@track isLoaded = false;
async loadLazyComponent() {
if (!this.isLoaded) {
// Dynamically import the module bundle (it reduces initial payload)
await import('c/heavyChild');
this.isLoaded = true; // now conditionally render the tag
}
}
}
// template
Note: dynamic import() fetches the module bundle. After import, creating the custom element with its tag (
3) Lazy-load third-party libraries with loadScript/loadStyle
For external libraries (charting libs, utility libs), use loadScript/loadStyle from lightning/platformResourceLoader and call them only when you need the library.
import { LightningElement } from 'lwc';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import LIB_RESOURCE from '@salesforce/resourceUrl/myLib';
export default class ChartWrapper extends LightningElement {
libInitialized = false;
async initializeLib() {
if (this.libInitialized) return;
await loadScript(this, LIB_RESOURCE + '/charts.min.js');
await loadStyle(this, LIB_RESOURCE + '/charts.css');
this.libInitialized = true;
this.renderChart();
}
}
4) Use IntersectionObserver for viewport-based loading
Load components or data when their container becomes visible in the viewport. This is great for long pages, dashboards, or lists.
import { LightningElement, track } from 'lwc';
export default class LazyViewport extends LightningElement {
@track visible = false;
renderedCallback() {
if (this.visible) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.visible = true; // will trigger template rendering or data fetch
observer.disconnect();
}
});
});
observer.observe(this.template.querySelector('.lazy-root'));
}
}
// template
5) Lazy-loading images and media
For images, use the native HTML attribute loading=”lazy” to defer offscreen images until they’re needed.
<img src="/assets/large-photo.jpg" alt="..." loading="lazy" />
6) Use UI container events (tabs, modals)
Many base components let you load content only when a tab becomes active or a modal opens. For example, load tab content on active event rather than rendering all tabs at once.
Best practices
- Lazy-load user-visible features that are not immediately needed on initial render.
- Combine conditional rendering with imperative data fetching (@wire vs imperative Apex) to avoid fetching unused data.
- Prefer native browser capabilities (loading=”lazy”) where available.
- Gracefully handle errors and provide loading indicators so users know something is happening.
- Don’t over-lazy: critical UI required for first interaction should load immediately.
Common pitfalls
- Overuse can increase perceived latency when users request data for the first time.
- Dynamic imports and resource loading should be cached where possible to avoid repeated downloads.
- Be careful with SEO and accessibility—ensure content loaded lazily remains discoverable if necessary.
Quick example: IntersectionObserver + dynamic import
import { LightningElement, track } from 'lwc';
export default class SmartLazy extends LightningElement {
@track show = false;
observer;
renderedCallback() {
if (this.show) return;
if (!this.observer) {
this.observer = new IntersectionObserver(async (entries) => {
if (entries[0].isIntersecting) {
await import('c/heavyChild');
this.show = true;
this.observer.disconnect();
}
});
this.observer.observe(this.template.querySelector('.root'));
}
}
}
// template
Conclusion
Lazy loading in LWC is a set of techniques—conditional rendering, dynamic import, platformResourceLoader, IntersectionObserver, and native browser features—to postpone work until it’s necessary. Use these patterns judiciously to improve initial load times and deliver a snappier experience for your users.
Leave a Reply