Loading UI

After you've applied all the optimizations, it is possible that your page still takes some time to load. This may be because you need to fetch and process a large amount of data in the backend, or may be it is a financial consideration and you do not want to spend too much on server upgrades.

Regardless, even if the server takes a while to process the request, you should not keep the users waiting without any responses. In this case, it is best to create a loading UI to tell the user that something is being done, instead of just letting the user wait and not see any visual changes.

Users are more likely to wait until the pages load if there is a meaningful loading UI that shows them something is being done, otherwise it may appear as if the web app is simply not responding.

Route segment loading UI

There are two ways to define loading UIs in Next.js. One approach is to create loading.jsx files for different route segments. Here's an example of how your file structure might look:

text
1src/app
2├── blog
3│   ├── loading.jsx
4│   └── page.jsx
5├── dashboard
6│   ├── loading.jsx
7│   ├── page.jsx
8│   └── users
9│       ├── loading.jsx
10│       └── page.jsx
11├── favicon.ico
12├── globals.css
13├── layout.jsx
14└── page.jsx

With this setup, can define different loading.jsx for different route segments. And whenever the corresponding route is visited, the loading UI will be displayed first, until the page.jsx finishes loading.

For example:

page.jsx

jsx
1export default async function Page() {
2  await new Promise((resolve) => setTimeout(resolve, 3000)); // Simulates data fetching
3
4  return <p className="text-gray-600">Hello, World!</p>;
5}

loading.jsx

jsx
1export default function Loading() {
2  return (
3    <div className="flex justify-center items-center h-screen">
4      <p>Loading...</p>
5    </div>
6  );
7}

In this case, because the page.jsx takes approximately 3 seconds to load, the loading.jsx will be displayed first.

Loading UI

Partial loading UI

However, this might be a very crude way to set up your loading UI, because it will wait until the entire page to finish loading, but in practice, different components could be loaded at different times.

Next.js allows you to fine-tune this setup by creating partial loading UI for different components of the webpage. Take a look at the following example:

text
1src
2├── components
3│   ├── mainComponent.jsx
4│   └── sidebarComponent.jsx
5└── app
6    ├── favicon.ico
7    ├── globals.css
8    ├── layout.jsx
9    ├── page.jsx
10    └── users
11        └── page.jsx
12

The page.jsx will be broken up into two separate components, the mainComponent and the sidebarComponent, which will be loaded at different times.

components/mainComponent.jsx

jsx
1export default async function Main() {
2  await new Promise((resolve) => setTimeout(resolve, 3000)); // Takes 3 seconds to load
3
4  return <p className="text-gray-600">Hello, World!</p>;
5}

components/sidebarComponent.jsx

jsx
1export default async function Sidebar() {
2  await new Promise((resolve) => setTimeout(resolve, 1500)); // Takes 1.5 seconds to load
3
4  return <p className="text-gray-600">This is a sidebar.</p>;
5}

Ideally, you'll want the sidebarComponent to be loaded first, and then the mainComponent. This will make sure the user sees visual changes as soon as possible, providing a better user experience.

This can be done with the <Suspense> component from React.js.

users/page.jsx

jsx
1import { Suspense } from "react";
2import Main from "@/components/mainComponent";
3import Sidebar from "@/components/sidebarComponent";
4
5export default async function Page() {
6  return (
7    <div className="m-4 flex gap-8">
8      <Suspense fallback={<p>Loading main component</p>}>
9        <Main />
10      </Suspense>
11
12      <Suspense fallback={<p>Loading sidebar component</p>}>
13        <Sidebar />
14      </Suspense>
15    </div>
16  );
17}

The <Suspense> component accepts a fallback prop, which defines the loading UI. As you probably have guessed, this UI will be displayed until the corresponding component inside <Suspense> is loaded, except this time, the two different components will be loaded separately.

Partial loading UI