Skip to main content

Command Palette

Search for a command to run...

Effortless Image Optimization in React: Boost Performance with Lazy Loading

Updated
•11 min read•View as Markdown
Effortless Image Optimization in React: Boost Performance with Lazy Loading
V

As an experienced software developer with over 4 years of experience in software development; Seeking the philosopher stone has always been my drive in whatever I do, and software development is not left out of the picture. I am constantly aware of the rapid change of wave flow in software development. This continually reminds me of the need to learn every day to keep up with these changes in software development. My lifelong principle in software development is that there is always a solution to a problem. I earnestly believe in this approach (or you can view it as a mindset) when approaching a new problem that seems daunting. I don't settle with the view that nothing can be solved; So in software development when faced with a stifling task, I seek means for a solution to that problem no matter how it will take me. In all of this, I see myself as someone who is a dedicated hard-working software developer who is constantly striving to improve my skills and knowledge in the field. I always put my client in focus when developing applications that will fill in the needs of their quest.

We easily get annoyed when we visit a website and its contents, mostly images, are not displayed or some borders showing the structure of the content is what is being displayed. Just like me, these occurrences easily infuriate me. Optimizing our web application is not just important for just the sake of it, It is important since it drives more clients to our web interface. So a lack of optimization will deter the user.

What if I show you a package that can easily, optimize our large resources using the Intersection Observer, What do I mean by this? I suggest that it optimizes our image when we look at the target resource.

In this article, I will walk you through optimizing our Images with the aid of react-lazy-load-image-component, so that, they are well-displayed to our consumers with low internet connections, to satisfy better search engine optimization.

What you will learn

I will try to walk us through the concept of lazy-loading, also the process of setting up our React application is not left out, and lastly, installing the react-lazy-load-image-component package.

How does the package work?

The React-lazy-load-image component tends to optimize our application by borrowing concepts from React lazy-loading and the intersection observation.

What is now lazy-loading?

Most Applications (Websites) do have a lot of resources that if downloaded once will lead to large consumption of data. Downloading the whole resource of a website tends to look wasteful. Why? I might not be interested in getting the whole content of the website, so downloading it at once goes against my will.

React Lazy Loading is just like a suspension. When we visit a website, we don't need to show everything at once to our customers. Some parts of the website might only be needed later. So, instead of loading everything at the beginning, we load the important parts first and save the rest for later when we need them.

Lazy loading is a concept used in React.Js to delay the download of large resources in our web applications, such as Images and scripts. When we lazy load a section of our application, we intend to optimize that section that was being lazy-loaded.

So the first instance a user gets to a website, the resources that make up the website have to be downloaded through a means of the internet connection. Images, and scripts, are not easily downloaded, so they are displayed without their actual contents.

Since we have gotten a glimpse of what lazy-loading entails let us proceed in creating our React application.

Developing our Application:

So we will proceed to develop our React application by using Vite rather than the contemporary create-react-app method. So let us proceed.


npm create vite@latest lazyloading -- --template react
cd lazyloading

In the script above we created a vite application residing in the lazyloading folder. The next step is creating and installing Install tailwindcss and its peer dependencies, then generating your tailwind.config.js and postcss.config.js files. Still, in the frontend folder, we type the following commands

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

We will then set up the tailwind.config.js file,

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

We can configure tailwind by adding these directives to our CSS file

@tailwind base;
@tailwind components;
@tailwind utilities

We have to install the necessary packages for our frontend application

npm i  react-router-dom react-lazy-load-image-component react-icons --legacy-peer-deps

Structuring the frontend:**

In the frontend src folder, we will structure to contain the following folders:

  1. components folder: This folder will house the individual building blocks of our pages. They are mostly Reactjs functional components.

  2. data folder: This folder will contain our menu list items.

  3. screen folder: This folder will contain our screen, which contains our component file.

We will have 1 component folder, the home component. We will also have 1 screen, the home screen.

Home component:

In the components folder, we will create a folder called home. This folder (home) will contain a home.js file and a index.jsx file. The index.jsx file will contain our menu listings the home.jsx file

home.js

import React, { useEffect } from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";

export default function Home() {
  return (
    <div className="flex w-full flex-col gap-4">

    </div>
  );
}

Explanation:

We created functional components, that display React elements. This element contains a single div, at the moment, that will display each of their contents respectively.

Index.jsx

import Home from "./home";

export default function HomeIndex() {

  return (
    <div>
      <Home />
    </div>
  );
}

So we have now imported the components into our index.js file; Next, we have to import this index.jsx file to our Home.js screen.

Home.jsx Screen

So within the screen folder, we will create our home.jsx file. This file will contain this code:


import React, { useEffect } from "react";
import HomeIndex from "../component/home";

export default function Home() {
  return (
    <div>
      <HomeIndex />
    </div>
  );
}

Since we have set up our screens, let us further create our App.js file

App.js

We have to clear the whole content of the App.js file and insert this content

import React, { lazy, Suspense } from "react";
import "./index.css";
import Home from "./screens/Home"
// // PaymentSuccess
export default function App() {
  const [height, setHeight] = useState(0);

  return (
    <div className="based" style={{ height }}>
      <Home/>
    </div>
  );
}

Explanation:

The initial App.js file contains pieces of stuff that are not necessary for our React Application. So we set the height to zero since React does have an extra height at the bottom.

So we have to set up our React application, we should proceed to develop our home component and show how to optimize our React Images. We have to first get the whole list of images. So we will create the data folder now.

Data (index.jsx)

Within the src folder, we will create a data folder that will contain our index.js file. This file will house our menu list data.

export const menudata = [
  // foods
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu262x-600x687.jpg",
    title: "Twice Cooked Pork",
    availabilityCount: 5,
    price: 21.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Main Course",
    userid: "669938972dc6dd45026faf27",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu272x-600x687.jpg",
    title: "Kung Pao Chicken",
    availabilityCount: 15,
    price: 21.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Main Course",
    userid: "669938972dc6dd45026faf27",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu282x-600x687.jpg",
    title: "California Rolls",
    availabilityCount: 10,
    price: 40.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Main Course",
    userid: "669938972dc6dd45026faf27",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu372x-600x687.jpg",
    title: "Braised Abalone",
    availabilityCount: 5,
    price: 52.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Main Course",
    userid: "669938972dc6dd45026faf27",
  },
  // Horse
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu192x-600x687.jpg",
    title: "Ahi Salmon Nigiri",
    availabilityCount: 8,
    price: 48.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Hors d’oeuvres",
    userid: "669938c72dc6dd45026faf28",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu402x-600x687.jpg",
    title: "Black Pudding",
    availabilityCount: 5,
    price: 25.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Hors d’oeuvres",
    userid: "669938c72dc6dd45026faf28",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu232x.jpg",
    title: "Cheeze & Fish On Bread",
    availabilityCount: 7,
    price: 25.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Hors d’oeuvres",
    userid: "669938c72dc6dd45026faf28",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu232x.jpg",
    title: "Temaki With Crab",
    availabilityCount: 5,
    price: 32.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "Hors d’oeuvres",
    userid: "669938972dc6dd45026faf27",
  },
  // desserts
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu392x-600x687.jpg",
    title: "Maple Mouse With Chickpeas",
    availabilityCount: 5,
    price: 22.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "desserts",
    userid: "66993a115341c637335b49b4",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/hero182x-1200x675.jpg",
    title: "champagne Drink",
    availabilityCount: 5,
    price: 16.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "DRINK & COCKTAIL",
    userid: "66993a115341c637335b49b4",
  },
  {
    image:
      "https://avada.website/restaurant/wp-content/uploads/sites/112/2020/01/menu492x-600x687.jpg",
    title: "litchi-bomb",
    availabilityCount: 10,
    price: 26.0,
    description:
      "Tmi nulla in consequat, ut. Metus, nullam scelerisque netus viverra dui pretium pulvinar. Commodo morbi amet.",
    category: "DRINK & COCKTAIL",
    userid: "66993a115341c637335b49b4",
  },
]

Let us move to our home components:

home.jsx

We will create our Image component. This component will contain our react-image-lazy-load component. It will take 2 props the src and the alt prop.

Image.jsx

import React from "react";
import { LazyLoadImage } from "react-lazy-load-image-component";
import "react-lazy-load-image-component/src/effects/blur.css";

const Image = ({ alt, src }) => (
  <LazyLoadImage
    alt={alt}
    effect="blur"
    wrapperProps={{
      style: { transitionDelay: "1s", height: "100%", width: "100%", display:"block" },
    }}
    src={src}
  />
);

export default Image;

Explanation:

In the first section, we imported the requisite files needed for the react-lazy-load-image-component.

import React from "react";
import { LazyLoadImage } from "react-lazy-load-image-component";
import "react-lazy-load-image-component/src/effects/blur.css";

We then created a functional component called Image. This function takes in two props the src prop and the alt prop. The src prop will contain the path to the image and the alt prop will contain text needed for clients with eye impairment.

Different effects can be utilized; such as the blur effect which will render a blur concept over the image structure. The black-and-white effect will produce a black and white (kind of a grey concept) around the image. The opacity effect will render a space for the image.

So since we created the Image component file, we will then utilize it in our home.jsx file.

We will create our menuList component file:

menulist.jsx

import { menudata } from "../data";
const Menulist = () => {
  const maincourse = menudata?.filter(
    (data) => data.category === "Main Course"
  );
  const houres = menudata?.filter((data) => data.category === "Hors d’oeuvres");
  // get desserts collections
  const desserts = menudata?.filter((data) => data.category === "desserts");
  const DRINK = menudata?.filter(
    (data) => data.category === "DRINK & COCKTAIL"
  );
  const menuList = [
    {
      title: " Main Course",
      menudata: maincourse,
    },
    {
      title: "Hors d’oeuvres",
      menudata: houres,
    },
    {
      title: "desserts",
      menudata: desserts,
    },
    {
      title: "DRINK & COCKTAIL",
      menudata: DRINK,
    },
  ];
  return (
    <div className="w-full py-24">
      <div className="w-[90%] max-w-[1100px] mx-auto flex flex-col gap-20">
        {menuList?.map((menu, index) => {
          return (
            <div className="w-full flex flex-col gap-16">
              {index !== 0 && (
                <div className="w-full min-h-[35rem] relative">
                  <div className="absolute w-full h-full">
                    <Image
                      src={menu?.menudata[0]?.image}
                      alt=""
                      className="absolute z-10 w-full h-full top-0 left-0 object-cover"
                    />
                  </div>
                  <div
                    style={{
                      background: "rgba(0,0,0,.5)",
                    }}
                    className="w-full absolute h-full top-0 z-20"
                  ></div>
                  <div className="absolute flex items-center z-40 justify-center w-full bottom-10">
                    <h4 className="text-center text-xl md:text-2xl family4 text-white">
                      {menu?.menudata[0]?.title} – ₦{menu?.menudata[2]?.price}
                    </h4>
                  </div>
                </div>
              )}
              <div className="w-[90%] max-w-[1100px] flex flex-col gap-20 mx-auto">
                <h3 className="text-3xl md:text-5xl family4 italic font-normal">
                  {menu?.title}
                </h3>
                <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-20">
                  {menu?.menudata?.map((data) => {
                    return (
                      <Link
                        to={`/restaurant/takeout/${data?.id}?category=${data?.category}`}
                        className="flex w-full group flex-col gap-8"
                      >
                        <div className="w-full h-52">
                          <Image
                            src={data?.image}
                            alt=""
                            className="w-full h-full object-cover"
                          />
                        </div>
                        <div className="flex w-full flex-col gap-4">
                          <div className="flex items-center justify-between w-full">
                            <h4 className="text-4xl group-hover:text-[var(--primary)] family3">
                              {data?.title}
                            </h4>
                            <h4 className="text-xl font-normal family4">
                              ₦{data?.price}
                            </h4>
                          </div>
                          <p className="text-lg leading-[1.5] family4">
                            {data?.description}
                          </p>
                        </div>
                      </Link>
                    );
                  })}
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default Menulist

Explanation:

So we created a component that will contain our menu listing. We filtered each of the menus based on their categories and then injected them into an array of objects with title and their respective menu data. This array of objects is then mapped to return a React element that displays the individual menus.

The images are being optimized with the react-lazy-load-image components. So once the images are brought into view, the individual images are optimized or lazy-loaded with the blur effects.

  <Image
   src={data?.image}
   alt=""
   className="w-full h-full object-cover"
 />

so let us bring these components down to the home.jsx file

import Image from "./Image";
import { menudata } from "../data";
import Menulist from './menulist'
const HomeIndex = () => {
  return (
    <div className=" w-full flex flex-col">
      <Banner
        image={
          "https://avada.website/restaurant/wp-content/uploads/sites/112/2019/12/page22x.jpg"
        }
        subtext={"Best Table in Town"}
        text={"Our Takeout Menu"}
      />
      <Menulist />
    </div>
  );
};
const Banner = () => {
  return (
    <div className="w-full h-[30rem] relative">
      <Image
        alt=""
        loading="lazy"
        src={
          "    https://avada.website/restaurant/wp-content/uploads/sites/112/2020/04/slider72x-scaled.jpg"
        }
        className="imagewrapper"
      />
      <div className="w- mx-auto flex items-center justify-center">
        <h4
          className="text-lg text-light uppercase text-white family1"
          style={{ letterSpacing: "1.5px", fontWeight: "normal" }}
        >
          Best Table in Town
        </h4>
        <h1 className="family3 text-7xl text-light uppercase text-white">
          Our Takeout Menu
        </h1>
      </div>
      <div className="absolute gradient z-[20] h-full w-full"></div>
    </div>
  );
};
export default  HomeIndex

Explanation:

So we have finally brought each of the components together to the home.jsx file. In the file above, we created a banner component that helped us to create a banner. We then returned both the menu list component and the banner component in a div.

From the final application, we can see that once we approach images for the first, a blur effect is produced. This is due to our package utilizing the Intersection Observer API.

Conclusion

In this tutorial, we have gone through a step-by-step in utilizing the react-lazy-load-image-component to lazy-load our images leading to an enriched user experience and an intuitive effect on our users.

In the beginning, we reached an understanding of what lazy-loading entails, its purpose, and its mode of action. we also reached to an understanding of how we can install the React application with Tailwind, and lastly how to use the package (react-lazy-load-image-component) to optimize our applications.