In this guide, we will learn how to implement a custom Next JS Loading Spinner component. This Loading Spinner can easily be customized according to your theme.
Also, we will discuss how to easily integrate it with Routing transition and HTTP calls as Axios-based Interceptors to indicate users with Spinner loader.
The Loading Spinner plays an important role in user-faced applications to indicate if there is any process currently going on. When we transition between pages or fetch data, it’s important to intimate the user about the ongoing process.
Let’s dive in and explore how to implement and customize a loading spinner in Next.js.
[lwptoc]
How to create Custome Loading Spinner in the Next js application?
Follow this step-by-step tutorial to create a customer Spinner loading component. Thereafter we will discuss how to use this Spinner component in navigation transition and watch HTTP calls using Axios based interceptor:
Step 1: Setup Next JS Application
Step 2:
Step 1: Setup Next JS Application
For easy to understand process, we will create a new Next JS application. If you already have one, just go with that and jump to next steps.
You can create a new Next.js application by running the following command in your terminal:
npx create-next-app@11 next-js-loading-spinner
This command uses npx
to run the create-next-app
to create a new Next.js application in a directory called next-js-loading-spinner.
Navigate into your new project directory:
cd next-js-loading-spinner
You can run the Next js application by executing the below command app application root folder:
npm run dev
Step 2: Creating the Spinner Component
Let’s create a new component for our spinner. In the components directory, create a new file called Spinner.js.
In this file, we will create a functional component that will return a simple div with a class named spinner. This div will act as our loading spinner:
// components/Spinner.js
import styles from "../styles/Spinner.module.css";
function Spinner() {
return (
<div className={styles.overlay}>
<div className={styles.spinner}></div>
</div>
);
}
export default Spinner;
Step 2: Styling the Spinner
Next, we will add some basic CSS styles to spinner. In the styles directory, create a new CSS file called Spinner.module.css.
In this file, add the following styles to make our div look like a spinner. We’ll use CSS animations to create a spinning effect.
/* styles/Spinner.module.css */
.overlay {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9999;
}
.spinner {
border: 16px solid #f3f3f3;
border-top: 16px solid #3498db;
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
The above style will create a spinner loader component which will look like this:
Step 3: Show Spinner on Page Routing / Transitions
Now we will deploy the spinner to be displayed during page transitions. We will use the useRouter
hook from Next.js, which allows us to access the router
object and its properties.
In the _app.js
file, import Spinner
component and the useRouter
hook. Then use the router.events
property to listen for route change events and display our spinner.
// pages/_app.js
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import Spinner from "../components/Spinner";
function MyApp({ Component, pageProps }) {
const router = useRouter();
const [loading, setLoading] = useState(false);
useEffect(() => {
const handleStart = () => {
setLoading(true);
};
const handleComplete = () => {
setLoading(false);
};
router.events.on("routeChangeStart", handleStart);
router.events.on("routeChangeComplete", handleComplete);
router.events.on("routeChangeError", handleComplete);
return () => {
router.events.off("routeChangeStart", handleStart);
router.events.off("routeChangeComplete", handleComplete);
router.events.off("routeChangeError", handleComplete);
};
}, [router]);
return (
<>
{loading && <Spinner />}
<Component {...pageProps} />
</>
);
}
export default MyApp;
In the above code, we are using the useState
and useEffect
hooks from React to manage our loading state and listen for route change events.
When a route change starts, we set loading
to true
, and when it completes or if there’s an error, we set loading
to false
, which removes the Spinner
from the screen.
Create Pages for Testing
Finally, let’s create two sample pages, Page1.js
and Page2.js
, and add links to navigate between them:
// pages/Page1.js
import Link from 'next/link';
function Page1() {
return (
<div>
<h1>Page 1</h1>
<Link href="/Page2">Go to Page 2</Link>
</div>
);
}
export default Page1;
// pages/Page2.js
import Link from 'next/link';
function Page2() {
return (
<div>
<h1>Page 2</h1>
<Link href="/Page1">Go to Page 1</Link>
</div>
);
}
export default Page2;
Update Index Page
Thereafter we will update the index.js page, which acts as a landing page for the new Next js application.
// pages/index.js
import Link from "next/link";
import { useAxios } from "../utils/axios";
import { useState } from "react";
function Home() {
const [axios, spinner] = useAxios();
const [data, setData] = useState(null);
const handleFetchData = async () => {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
setData(response.data);
};
return (
<div>
<h1>Home</h1>
<Link href="/Page1">Go to Page 1</Link>
<br />
<Link href="/Page2">Go to Page 2</Link>
<br />
<button onClick={handleFetchData}>Fetch Data</button>
{spinner}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}
export default Home;
This file will have links to page 1 and page 2. Now whenever you navigate from one page to another, you will see the Spinner loading element in your Next js application.
Other than this you will also notice a button to trigger an HTTP call. Currently, this call will not display the spinner loader, as we still need to introduce the HTTP call interceptor. Which we will discuss in the next section.
Step 4: Show Spinner on HTTP Calls using Axios Interceptor
In this step, you will how to add an HTTP interceptor in the Next js application using the Axios library. This HTTP interceptor will be created once and watch each and every HTTP call of every type including POST, GET etc to show Spinner Loader.
This Axios interceptor will keep a watch on each and every HTTP remote API call and show/ hide the Spinner Loader component which we create as a custom component.
Follow these steps to set up the Next js app to create an Axios interceptor and use it in HTTP calls:
First, install axios
using npm:
npm install axios
Then, create a new axios.js
file inside the utils folder at your application root folder:
// utils/axios.js
import axios from 'axios';
import { useState } from 'react';
import Spinner from '../components/Spinner';
const instance = axios.create();
export const useAxios = () => {
const [loading, setLoading] = useState(false);
instance.interceptors.request.use(function (config) {
setLoading(true);
return config;
}, function (error) {
setLoading(false);
return Promise.reject(error);
});
instance.interceptors.response.use(function (response) {
setLoading(false);
return response;
}, function (error) {
setLoading(false);
return Promise.reject(error);
});
return [instance, loading ? <Spinner /> : null];
};
export default instance;
In this file, we’re creating a custom useAxios
hook that returns an axios instance and a loading spinner. The Axios instance is configured with request and response interceptors that set the loading state to true
before a request and to false
after a response or an error.
You can then use this useAxios
hook in your components to make HTTP requests:
// pages/index.js
import Link from 'next/link';
import { useAxios } from '../utils/axios';
import { useState } from 'react';
function Home() {
const [axios, spinner] = useAxios();
const [data, setData] = useState(null);
const handleFetchData = async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
setData(response.data);
};
return (
<div>
<h1>Home</h1>
<Link href="/Page1">Go to Page 1</Link>
<br />
<Link href="/Page2">Go to Page 2</Link>
<br />
<button onClick={handleFetchData}>Fetch Data</button>
{spinner}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}
export default Home;
Step 5: Customizing the Loading Spinner
Now that we have a basic loading spinner in place, let’s look at how we can customize it to better match the look and feel of our application. Customizing the loading spinner can be done primarily through CSS.
Changing the Size of the Spinner
The size of the spinner can be adjusted by changing the width
and height
properties in the CSS.
.spinner {
...
width: 60px;
height: 60px;
...
}
Changing the Color of the Spinner
The colour of the spinner can be changed by modifying the border-top
property.
.spinner {
...
border-top: 16px solid red;
...
}
Changing the Speed of the Spinner
Speed of the spinning animation can be adjusted by changing the duration of the spin
animation in the CSS. A smaller value will make the spinner spin faster, while a larger value will make it spin slower:
.spinner {
...
animation: spin 1s linear infinite; /* Faster */
...
}
Conclusion
We discussed how to easily create a custom Spinner Loader component in the Next js application. The Spinner loading component is used in the Router navigation to indicate users while page transitions.
The most important use case is to display the loader Spinner when we make an HTTP Call to fetch data. For that, we create an Axios-based interface, which can easily be created once and used everywhere to show the Spinner loader whenever any kind of HTTP call is triggered.
The Spinner component we created, can easily be customized with your own style and looks. The Spinner is shown as an overlay in the centre of page with a backdrop to prevent users to take any action till the current action is completed.
There are also many other ways to indicate users about the loading process like using the linear progress bar on the page top like YouTube using the NProgress which you can check here.
Leave a Reply