DEV Community

Cover image for Lazy Loading Isn’t Just for Images: What Else Can You Delay?
Pradeep
Pradeep

Posted on

Lazy Loading Isn’t Just for Images: What Else Can You Delay?

When we talk about lazy loading, most developers think of one thing:

<img src="cat.jpg" loading="lazy" />
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

✅ 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);
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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'));
Enter fullscreen mode Exit fullscreen mode

🧠 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();
});
Enter fullscreen mode Exit fullscreen mode

Or delay them slightly:

setTimeout(() => {
  loadAnalytics();
}, 3000);
Enter fullscreen mode Exit fullscreen mode

🔌 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;
}
Enter fullscreen mode Exit fullscreen mode

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'));
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
nevodavid profile image
Nevo David

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?

Collapse
 
javascriptwizzard profile image
Pradeep

Thanks @nevodavid !
It completely depends on the specific usecase you are catering to.
Consider factors like:

  1. SPA vs PWA vs Traditional Navigation
  2. Mobile vs Tablet vs Desktop app
  3. High bandwidth vs low bandwidth users and more...
Collapse
 
abhinavshinoy90 profile image
Abhinav Shinoy

Once again an awesome post! Thanks @javascriptwizzard !

Collapse
 
javascriptwizzard profile image
Pradeep

Thanks @abhinavshinoy90 !

Collapse
 
abhinavshinoy90 profile image
Abhinav Shinoy

BTW, Does requestIdleCallback wait until window load or simply whenever the main thread is idle?

Collapse
 
javascriptwizzard profile image
Pradeep

It doesnt wait for window load @abhinavshinoy90 it gets triggered whenever main thread is idle.