Lazy Loading in React

Lazy loading in React.js is an effective way to enhance your app's performance by loading components only when they are needed. Rather than requiring users to wait while the entire application loads upfront—regardless of what they actually need—lazy loading allows you to divide your code into smaller, more manageable chunks. These chunks are then loaded on demand as users navigate through your app.

This approach significantly reduces the initial load time, ensuring that your app starts up faster and only the necessary data is downloaded. Users interact with your app more smoothly, as they're not burdened with loading content they aren’t immediately accessing.

By implementing lazy loading, you not only improve your app's speed but also optimize resource usage, delivering a more responsive and efficient user experience. It’s a smart strategy that aligns with modern best practices in web development.

There are several types of lazy loading we will discuss in this blog.

1. Dynamic Lazy Loading

import React, { lazy, Suspense } from "react";
import Header from "@/app/components/common/header/Header";

//dynamically importing the Post component
const Post = lazy(() => import("../../components/util-components/Post"));

const DynamicLazyLoad = () => {
  return (
    <div>
      {/* Using Suspense to render fallback while Post is dynamically loading */}
      <Header color="red">Dynamic Lazy Load</Header>
      <Suspense fallback={<div>Loading...</div>}>
        <Post />
      </Suspense>
    </div>
  );
};

export default DynamicLazyLoad;

Post Header

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam vehicula, urna eu efficitur dapibus, tortor sapien ultricies arcu, nec elementum magna ligula sit amet odio. Praesent luctus, erat eu aliquet vulputate, erat nisi scelerisque ligula, sit amet euismod elit nulla eget neque. Curabitur scelerisque felis nec ante condimentum, ac lobortis lorem viverra. Sed vel sagittis purus. Cras in ex arcu. Duis sagittis, felis vel vehicula bibendum, libero lectus pellentesque elit, in dapibus metus nisl a elit. Integer et ex id arcu vehicula lacinia. Integer consectetur ipsum non purus eleifend, at cursus justo sagittis. Nam fermentum magna sit amet urna ultricies, nec

Post Footer

2. Lazy Load On Interaction


import React, { useState } from "react";
import Header from "@/app/components/common/header/Header";

const LazyLoadOnInteraction = () => {
  const [Post, setPost] = useState(null);

  const handleClick = () => {
    import("../../util-components/Post").then((module) => {
      setPost(() => module.default);
    });
  };

  return (
    <div>
      <Header color="red"> Lazy Loading on Interaction </Header>
      {Post ? <Post /> : <button onClick={handleClick}>Load Post</button>}
    </div>
  );
};

export default LazyLoadOnInteraction;
          

Lazy Loading on Interaction

3. Lazy Loading with Intersection Observer


import React, { useState, useRef, lazy, Suspense } from "react";
import useIntersectionObserver from "../../hooks/useIntersectionObserver";
import Header from "@/app/components/common/header/Header";

const Post = lazy(() => import("../../components/util-components/Post"));

const LazyIntersectionObserver = () => {
  const [shouldRenderPost, setShouldRenderPost] = useState(false);

  const postRef = useRef(null);

  const handleIntersect = ([entry]) => {
    if (entry.isIntersecting) {
      setTimeout(() => {
        setShouldRenderPost(true);
      }, 1000); //setTimeout is only for loading effect - dont use in production
    }
  };

  useIntersectionObserver(postRef, handleIntersect, { treshhold: 0 });

  return (
    <div>
      <Header color="red">Lazy Loading with the Intersection Observer</Header>
      <div style={{ height: "500px" }}>Some Content before the post</div>
      <div ref={postRef}>
        {shouldRenderPost ? (
          <Suspense fallback={<div>Loading Suspense ...</div>}>
            <Post />
          </Suspense>
        ) : (
          <div>Loading...</div>
        )}
      </div>
      <div style={{ height: "500px" }}>Some Content after the post</div>
    </div>
  );
};

export default LazyIntersectionObserver;

useIntersectionObserver implementation

import { useEffect } from "react";

const useIntersectionObserver = (ref, callback, options) => {
  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      callback(entries);
    }, options);

    const element = ref.current;
    if (element) {
      observer.observe(element);
    }

    return () => {
      if (element) {
        observer.unobserve(element);
      }
    };
  }, [ref, callback, options]);
};

export default useIntersectionObserver;

Lazy Loading with the Intersection Observer

Some Content before the post
Loading...
Some Content after the post

4. Route-Based Lazy Loading

When using React Router, you can lazy load components associated with specific routes. This ensures that only the components necessary for the current route are loaded, reducing the initial bundle size.

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import React, { Suspense } from 'react';

const Home = React.lazy(() => import('./Home'));
const About = React.lazy(() => import('./About'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </Router>
  );
}

5. Image Lazy Loading

Native HTML Loading Attribute: The simplest method for lazy loading images is to use the loading="lazy" attribute on the '<img>' tag. This instructs the browser to delay loading the image until it’s in or near the viewport.

<img src="image.jpg" alt="Example" loading="lazy" />

Useful resources on Lazy Load

React docs: Lazy Load
All BlogsGo To Top