Implementing Lazy Loading for Components in Next.js
- Md. Saad
- August 11, 2024
What is lazy Loading?
Lazy loading is an optimization technique for a website or web app. Loading the whole website at a time might take time and result in a good user experience. So, instead of waiting for the whole page or application to get loaded into the browser, we may need to load some parts of the website immediately and some parts to load only when it is required. This technique of loading the necessary portions only and delaying the rest until the user requests it is called Lazy Loading.
Lazy Loading in Next.js
Lazy Loading in Next JS is used to improve the application's loading performance. It works by decreasing the amount of JavaScript required for a route. Lazy loading fetches resources asynchronously. Thus, We can delay the loading of the client components and imported libraries until they are needed which allows the initial page load to be faster. In this article, we will discuss the approach of Lazy Loading in NextJS:
There are two ways you can implement lazy loading in Next.js:
- Using dynamic imports with the next/dynamic package.
- Using a combination of React.lazy() and Suspense.
- Note: Remember that lazy loading applies to client components, not server components. If you dynamically import a server component, only its child client components will be lazy-loaded.
Setting up a Next.js Application
The first and foremost step to begin with is creating a Next JS App. Use the following command to create a next.js app.
npx create-next-app@latest app-name
As it is not an article on creating the next.js app, we will not discuss it in detail. If you want to read it in detail follow the link.
We have used Tailwind CSS for styling in the examples. To know more about Tailwind Configuration in Next Js, you can read this article
With dynamic import and next/dynamic
next/dynamic uses the functionalities of both React.lazy() and Suspense. It is preferable to add Lazy Loading to Next.js. Furthermore, it works similarly in the app and page directories to allow for incremental migration.
Let’s demonstrate it in an example. We have already created a next.js project. We will use the project to illustrate the project.
First Open the project in any code editor. Now create a folder called components under the app/ directory. Finally, create a React component called LazyComp.jsx under the app/components/ directory with the following code:
const LazyComp = () => {
return (
<div className="flex flex-col">
<p className="text-xl my-1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur magna in odio luctus, aliquam vehicula
nisl porttitor. Maecenas non interdum est. Maecenas at condimentum orci. Aliquam ultrices vestibulum ligula. Nam
suscipit quis mi imperdiet placerat. Mauris facilisis justo lacus, in tincidunt lectus scelerisque ac. Phasellus
sodales justo vel magna porttitor, eget sollicitudin diam molestie. Phasellus ut lorem pellentesque, iaculis
purus ut, tempor erat. Nunc a volutpat mi. Pellentesque habitant morbi tristique senectus et netus et malesuada
fames ac turpis egestas. In ultrices tristique nibh, sed mattis urna sodales a. Aliquam non finibus odio.
Curabitur convallis a nulla eget interdum. Sed non laoreet magna. Phasellus consequat dolor quis justo
consequat, eu maximus sem posuere. Fusce lobortis quis tortor at lobortis.
</p>
<p className="text-xl my-1">
Suspendisse in nisl rhoncus, egestas est nec, tincidunt orci. Vestibulum ante ipsum primis in faucibus orci
luctus et ultrices posuere cubilia curae; Aenean rhoncus, dolor at cursus imperdiet, felis neque tristique elit,
rutrum laoreet neque elit sed tellus. Donec ut ligula turpis. Suspendisse potenti. Sed in nisi nulla. Nunc vitae
mollis elit. Phasellus eget malesuada diam. Suspendisse accumsan vel ipsum vel sodales. Sed bibendum euismod
lobortis. Nulla non diam eget orci dapibus ullamcorper a quis odio. Sed eget mauris metus. Maecenas a risus
dolor. Integer non justo tincidunt lacus tincidunt eleifend.
</p>
</div>
);
};
export default LazyComp;
In this example, we have created a simple React component called LazyComp. We have also default exported the component to import it elsewhere.
Now, create another file called DynamicLoad.jsx under the app/components/ directory with the following code:
"use client";
import {useState} from "react";
import dynamic from "next/dynamic";
const DynamicLazyLoad = dynamic(() => import("./LazyComp"), {
loading: () => <h1>Loading Using Next Js Dynamic Load ...</h1>,
});
const DynamicLoad = () => {
const [shown, setShown] = useState(false);
return (
<div className="flex flex-col m-8 w-[350px]">
<h2 className="text-2xl my-1">Next JS Dynamic Loading</h2>
<button
className="bg-slate-600 text-black rounded p-1"
onClick={() => setShown(!shown)}
>
Load Using Next JS Dynamic Load
</button>
{shown && <DynamicLazyLoad />}
</div>
);
};
export default DynamicLoad;
In this example:
- We have created a client component called DynamicLoad using the "use client" directive.
- We have imported the useState hook for managing a toggle state, and the dynamic function from the next/dynamic for the lazy loading of the component we created before.
- The dynamic function accepts a function as an argument that returns the imported component. The dynamic() function returns the lazily loaded component instance, which is DynamicLoad (could be any name). We also added a custom loading message by passing an optional configuration object as an argument to the function.
- In the JSX, we have a toggle button that shows and hides the LazyTom component.
- Finally, we have default exported the DynamicLoad component.
Finally, open the page.js file in the app/ directory and replace the content with the following code:
import DynamicLoad from "@/components/DynamicLoad";
export default function Home() {
return (
<div className="flex flex-wrap justify-center ">
<DynamicLoad />
</div>
);
}
In the example, we have just imported the DynamicLoad component and used it in its JSX.
Now start your application using the following command.
npm run dev
This will open the app in the localhost:3000 port. Now, click on the button. You should see a loading message for a moment and then the component will be loaded.
That’s it. This is how the dynamic function from next/dynamic loads a component lazily. Now
Let's discuss the other method using React.lazy() and Suspense.
With React.lazy() and Suspense
To discuss this technique, Like the last time, let’s create another ReactSuspenseLoad.jsx component under the app/components/ folder. Now, add the following codes in the components:
"use client";
import React, {useState, Suspense} from "react";
const SuspenseLazyLoad = React.lazy(() => import("./LazyComp"));
const ReactSuspenseLoad = () => {
const [shown, setShown] = useState(false);
return (
<div className="flex flex-col m-8 w-[450px]">
<h2 className="text-2xl my-1">React Suspense Loading</h2>
<button
className="bg-slate-600 text-black rounded p-1"
onClick={() => setShown(!shown)}
>
Load Using React Suspense
</button>
{shown && (
<Suspense fallback={<h1>Loading Using React Suspense</h1>}>
<SuspenseLazyLoad />
</Suspense>
)}
</div>
);
};
export default ReactSuspenseLoad;
In this example, we have created a simple React component called LazyComp. We have also default exported the component to import it elsewhere.
Now, create another file called DynamicLoad.jsx under the app/components/ directory with the following code:
"use client";
import {useState} from "react";
import dynamic from "next/dynamic";
const DynamicLazyLoad = dynamic(() => import("./LazyComp"), {
loading: () => <h1>Loading Using Next Js Dynamic Load ...</h1>,
});
const DynamicLoad = () => {
const [shown, setShown] = useState(false);
return (
<div className="flex flex-col m-8 w-[350px]">
<h2 className="text-2xl my-1">Next JS Dynamic Loading</h2>
<button
className="bg-slate-600 text-black rounded p-1"
onClick={() => setShown(!shown)}
>
Load Using Next JS Dynamic Load
</button>
{shown && <DynamicLazyLoad />}
</div>
);
};
export default DynamicLoad;
In This example, we have followed the same steps as the previous one. But, instead of Dynamic() import from Next JS, we have used react Suspense. We also add a loading message using React Suspense Fallback. Finally, we have default exported the ReactSuspenseLoad component.
Now test it by importing it into the page.js file and adding the component to its JSX.
import DynamicLoad from "@/components/DynamicLoad";
import ReactSuspenseLoad from "@/components/ReactSuspenseLoad";
export default function Home() {
return (
<div className="flex flex-wrap justify-center">
<DynamicLoad />
<ReactSuspenseLoad />
</div>
);
}
Now, you'll see another component appear on the user interface with a button to load Load Using React Suspense. Now, click on the button. You should see a loading message for a moment and then the component will be loaded.
Importing Named Components
So far, we have imported components that were exported with default export. But in Javascript as well as React we can export a module using two methods. One is with the default export and another is without the default export which is called a named export. Let’s illustrate how to import named export components for lazy loading.
Let’s create a named component NamedLazyComp.jsx component under the app/components/ folder. Now, add the following codes in the component:
export const NamedLazyComp = () => {
return (
<div className="flex flex-col">
<p className="text-xl my-1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc consectetur magna in odio luctus, aliquam vehicula
nisl porttitor. Maecenas non interdum est. Maecenas at condimentum orci. Aliquam ultrices vestibulum ligula. Nam
suscipit quis mi imperdiet placerat. Mauris facilisis justo lacus, in tincidunt lectus scelerisque ac. Phasellus
sodales justo vel magna porttitor, eget sollicitudin diam molestie. Phasellus ut lorem pellentesque, iaculis
purus ut, tempor erat. Nunc a volutpat mi. Pellentesque habitant morbi tristique senectus et netus et malesuada
fames ac turpis egestas. In ultrices tristique nibh, sed mattis urna sodales a. Aliquam non finibus odio.
Curabitur convallis a nulla eget interdum. Sed non laoreet magna. Phasellus consequat dolor quis justo
consequat, eu maximus sem posuere. Fusce lobortis quis tortor at lobortis.
</p>
<p className="text-xl my-1">
Suspendisse in nisl rhoncus, egestas est nec, tincidunt orci. Vestibulum ante ipsum primis in faucibus orci
luctus et ultrices posuere cubilia curae; Aenean rhoncus, dolor at cursus imperdiet, felis neque tristique elit,
rutrum laoreet neque elit sed tellus. Donec ut ligula turpis. Suspendisse potenti. Sed in nisi nulla. Nunc vitae
mollis elit. Phasellus eget malesuada diam. Suspendisse accumsan vel ipsum vel sodales. Sed bibendum euismod
lobortis. Nulla non diam eget orci dapibus ullamcorper a quis odio. Sed eget mauris metus. Maecenas a risus
dolor. Integer non justo tincidunt lacus tincidunt eleifend.
</p>
</div>
);
};
In this example, everything's the same as in the previous LazyComp. But, Instead of the default export we have used the named export.
Now, create another NamedLoad.jsx component in the app/components/ folder. Now, add the following codes in the component:
"use client";
import {useState} from "react";
import dynamic from "next/dynamic";
const NamedLazyLoad = dynamic(() => import("./NamedLazyComp").then((mod) => mod.NamedLazyComp), {
loading: () => <h1>Loading Importing Named component ...</h1>,
});
const NamedLoad= () => {
const [shown, setShown] = useState(false);
return (
<div className="flex flex-col m-8 w-[450px]">
<h2 className="text-2xl my-1">Importing Named component</h2>
<button
className="bg-slate-600 text-black rounded p-1"
onClick={() => setShown(!shown)}
>
Load Named Component
</button>
{shown && <NamedLazyLoad />}
</div>
);
};
export default NamedLoad;
Like before we have used dynamic from next/dynamic to import the file. But, we are resolving the promise explicitly from the import("./NamedLazyComp") function using the .then() handler function. We get the module first and then pick the exported component by its actual name – that is NamedLazyLoad in this case.
Now, test it again. Import the code on the page.jsx like the previous two examples.
import DynamicLoad from "@/components/DynamicLoad";
import NamedLoad from "@/components/NamedLoad";
import ReactSuspenseLoad from "@/components/ReactSuspenseLoad";
export default function Home() {
return (
<div className="flex flex-wrap justify-center">
<DynamicLoad />
<ReactSuspenseLoad />
<NamedLoad />
</div>
);
}
Now, you'll see another component appear on the user interface with a button to load the Load Named Component. Now, click on the button. You should see a loading message for a moment and then the component will be loaded.
Importing Server Components
By default, server components are already code split and the loading aspect has already been taken care of by Next.js. So, it is unnecessary to add lazy loading in the server component. However, we can add it if we want. But it will not create any effects on the server component.
Remember, if a server component is imported which has several client components as children, only client components will be lazy-loaded. However, there won't be any effect on the actual (parent) server component.
That's all for now. We have successfully discussed the necessary steps for implementing Lazy Loading in Next.js. I hope you can implement the techniques you learned from this article. Yet, you have queries?? Feel free to contact StaticMania.