Introduction
Loaders improve user experience by indicating when content is being fetched or processed. Here's a compressed guide to adding loaders in a Next.js application.
Creating a Loader Component
Basic Loader Component
Create a simple spinner component.
// components/Loader.js
import styles from './Loader.module.css';
const Loader = () => (
<div className={styles.loader}>
<div className={styles.spinner}></div>
</div>
);
export default Loader;
Styling the Loader
Add CSS for the spinner.
/* components/Loader.module.css */
.loader {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.spinner {
border: 8px solid rgba(0, 0, 0, 0.1);
border-top: 8px solid #3498db;
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 1.5s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Using the Loader Globally
Page Transitions
Show a loader during page transitions.
// pages/_app.js
import { useEffect, useState } from 'react';
import Router from 'next/router';
import Loader from '../components/Loader';
function MyApp({ Component, pageProps }) {
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);
};
}, []);
return (
<>
{loading && <Loader />}
<Component {...pageProps} />
</>
);
}
export default MyApp;
Data Fetching
Show a loader while fetching data.
// pages/index.js
import { useEffect, useState } from 'react';
import Loader from '../components/Loader';
const HomePage = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
const res = await fetch('/api/data');
const result = await res.json();
setData(result);
setLoading(false);
};
fetchData();
}, []);
return (
<div>
{loading ? <Loader /> : <div>Data: {JSON.stringify(data)}</div>}
</div>
);
};
export default HomePage;
Using the Loader Locally
Component-Level Loader
Implement a loader within a specific component.
// components/DataFetcher.js
import { useEffect, useState } from 'react';
import Loader from './Loader';
const DataFetcher = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
const res = await fetch('/api/data');
const result = await res.json();
setData(result);
setLoading(false);
};
fetchData();
}, []);
return (
<div>
{loading ? <Loader /> : <div>Data: {JSON.stringify(data)}</div>}
</div>
);
};
export default DataFetcher;
Best Practices
- Design: Keep loaders lightweight and visually appropriate.
- Performance: Ensure loaders do not impact performance negatively.
- User Experience: Provide clear feedback to users during loading states.
Summary
Adding loaders in Next.js enhances the user experience by providing visual cues during data fetching and page transitions. Use the Loader component globally or locally to manage loading states effectively.