When we talk about lazy loading, most developers think of one thing:
<img src="cat.jpg" loading="lazy" />
It’s an awesome performance win — only load images when they're needed.
But what if I told you lazy loading doesn’t stop at images?
There’s a whole world of assets, components, and even logic that you can delay to give users a faster, smoother experience.
Let’s dig in. 👇
🧱 1. Lazy Loading JavaScript Modules
Modern bundlers like Webpack, Vite, and Rollup support dynamic import()
for splitting your JS into chunks and loading them on demand.
Example:
button.addEventListener("click", async () => {
const { showModal } = await import("./modal.js");
showModal();
});
✅ Benefits:
- Smaller initial bundle
- Faster initial load
- Only load what the user interacts with
🎨 2. Lazy Loading CSS
Yes — you can lazy load styles too.
Example (with JavaScript):
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "extra-styles.css";
document.head.appendChild(link);
Useful for styles needed only after a user navigates to a specific section or opens a modal.
💡 Pro Tip: Tools like vite-plugin-style-import
or Webpack’s code splitting
can help automate this.
🧩 3. Lazy Loading Web Components or UI Sections
Using frameworks or raw JavaScript, you can defer loading complex components:
Example with React:
const Comments = React.lazy(() => import('./Comments'));
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
Example without React (vanilla JS):
Load a custom element only when it enters the viewport:
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
import('./fancy-widget.js');
observer.disconnect();
}
});
});
observer.observe(document.querySelector('#widget'));
🧠 4. Lazy Execution of Expensive Logic
Not everything needs to run at page load.
- Analytics
- Animations
- Non-critical API calls
Instead, defer them until idle time:
window.requestIdleCallback(() => {
expensiveNonCriticalWork();
});
Or delay them slightly:
setTimeout(() => {
loadAnalytics();
}, 3000);
🔌 5. Lazy Loading Fonts
Fonts can be large and render-blocking if not handled well.
Use font-display: swap
in your @font-face
CSS rule:
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}
This tells the browser to render fallback text immediately and swap in your custom font once it's loaded.
🗺 6. Lazy Loading Routes in SPAs
If you're building a single-page app with client-side routing, don’t load all pages upfront.
With tools like React Router or Vue Router:
const AboutPage = React.lazy(() => import('./About'));
Each route gets its own chunk — users only download the code they need.
🧩 7. Lazy Hydration (Advanced)
For sites using static or SSR (like Next.js, Astro, or Qwik), consider hydrating interactivity lazily, either:
- On user interaction (e.g., click, scroll)
- When components enter the viewport
This can drastically reduce time to interactive (TTI).
⚠️ Caveats
Lazy loading is powerful, but:
- Don't overdo it: delaying too much can make your app feel sluggish or unresponsive.
- Measure: Use tools like Lighthouse, WebPageTest, or Chrome DevTools to ensure lazy loading is helping, not hurting.
- Test fallback behavior: Always account for unsupported features or timing issues.
🧪 Final Thoughts
Lazy loading is no longer just an image optimization trick — it’s a mindset.
Load only what the user needs, when they need it.
It improves performance, saves bandwidth, and enhances UX — especially on mobile or slow connections.
So next time you ship a feature, ask yourself:
Can this wait just a little bit longer?
Chances are… it probably can.
Top comments (6)
pretty cool breakdown tbh - i always wonder if i’m holding too much stuff back or not enough, you ever feel like there’s a good way to find that balance?
Thanks @nevodavid !
It completely depends on the specific usecase you are catering to.
Consider factors like:
Once again an awesome post! Thanks @javascriptwizzard !
Thanks @abhinavshinoy90 !
BTW, Does requestIdleCallback wait until window load or simply whenever the main thread is idle?
It doesnt wait for window load @abhinavshinoy90 it gets triggered whenever main thread is idle.