Lazy Loading in LWC — What it is and How to Implement It

Introduction

Lazy loading in Lightning Web Components (LWC) is a performance technique that defers loading of code, resources, or UI until they’re actually needed. This reduces initial bundle size, speeds up page load, and improves perceived performance — especially for large Salesforce apps.

Why use lazy loading in LWC?

Use lazy loading to:

– Improve initial render speed by loading non-critical code later.

– Reduce memory and CPU usage for components not immediately used.

– Load third-party libraries only when required.

Core patterns for lazy loading in LWC

There are several practical approaches to lazy loading in LWC:

1) Conditional rendering (if:true / if:false)

Simple and effective: delay rendering a child component until a condition becomes true. This prevents the child from being created at initial render.

<template if:true={showChild}>
<c-heavy-child></c-heavy-child>
</template>

Set showChild to true in an event handler. Note: this does not split JS bundles — the component’s code may still be in the initial bundle. For true code splitting use dynamic import.

2) Dynamic import (code-splitting)

Dynamic import loads a module only when needed and enables the platform to create a separate bundle for that module. In LWC you can dynamically import utility modules or component constructors and then instantiate components programmatically.

import { LightningElement, createElement } from 'lwc';

export default class ParentCmp extends LightningElement {
handleLoadChild() {
import('c/lazyChild')
.then(module => {
const LazyCtor = module.default;
const el = createElement('c-lazy-child', { is: LazyCtor });
this.template.querySelector('.container').appendChild(el);
})
.catch(error => {
// handle load error
console.error('Failed to load component', error);
});
}
}

This approach creates a separate chunk for c/lazyChild, improving initial load time.

3) Loading 3rd-party libraries on demand (loadScript / loadStyle)

For static resources stored in static resources, use loadScript and loadStyle from lightning/platformResourceLoader. This defers loading large libraries until they are required.

import { LightningElement } from 'lwc';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import LIB from '@salesforce/resourceUrl/myLib';

export default class Example extends LightningElement {
connectedCallback() {
// load when needed (maybe on first interaction)
loadScript(this, LIB + '/dist/lib.min.js')
.then(() => {
// library available
})
.catch(error => console.error(error));
}
}

4) Lazy-loading based on viewport (IntersectionObserver)

For components or images that should load when they appear in the viewport, use the browser’s IntersectionObserver to trigger rendering or dynamic imports.

connectedCallback() {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadHeavy();
observer.disconnect();
}
});
});
observer.observe(this.template.querySelector('.sentinel'));
}

loadHeavy() {
import('c/heavyComponent').then(m => { /* create component */ });
}

Best practices and pitfalls

– Prefer dynamic import for true code splitting; conditional templates only delay creation but may not split bundles.

– Cache loaded modules or libraries to avoid repeated network calls.

– Always handle errors and show a lightweight placeholder or spinner while loading.

– Check Locker Service and CSP: loadScript/loadStyle must use static resources served via Salesforce static resource; you cannot load arbitrary remote scripts due to CSP.

– Lazy-loading improves perceived performance but can increase complexity. Use it where the performance benefit justifies the added code paths.

Summary

Lazy loading in LWC can be implemented with conditional rendering, dynamic imports for code-splitting, on-demand library loading via platformResourceLoader, and viewport-based loading using IntersectionObserver. Combining these patterns helps create fast, responsive Salesforce UIs.