Explain Adding a Loader in Next.js

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.