Detect when the page/route is loading with custom hook - NextJS

Published May 26, 2021

Updated September 28, 2021

Update: NextJS have updated the recommended way to detect loading since version 11. The examples have been updated. Old example can be found at the bottom of the post.

Within NextJS's router there are events that can be captured using Router from next/router.

Below is a simple custom hook that gives you a loading state boolean that you can use anywhere within your NextJS project.

TypeScript

import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useState } from 'react';

const useLoading = (): boolean => {
    const [loading, setLoading] = useState<boolean>(false);
    const router = useRouter();

    useEffect(() => {
        const handleIsLoading = () => {
            setLoading(true);
        };

        const handleNotLoading = () => {
            setLoading(false);
        };

        router.events.on('routeChangeStart', handleIsLoading);
        router.events.on('routeChangeComplete', handleNotLoading);
        router.events.on('routeChangeError', handleNotLoading);

        return () => {
            router.events.off('routeChangeStart', handleIsLoading);
            router.events.off('routeChangeComplete', handleNotLoading);
            router.events.off('routeChangeError', handleNotLoading);
        };
    }, [router]);

    return loading;
};

export default useLoading;

JavaScript

import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useState } from 'react';

const useLoading = () => {
    const [loading, setLoading] = useState(false);
    const router = useRouter();

    useEffect(() => {
        const handleIsLoading = () => {
            setLoading(true);
        };

        const handleNotLoading = () => {
            setLoading(false);
        };

        router.events.on('routeChangeStart', handleIsLoading);
        router.events.on('routeChangeComplete', handleNotLoading);
        router.events.on('routeChangeError', handleNotLoading);

        return () => {
            router.events.off('routeChangeStart', handleIsLoading);
            router.events.off('routeChangeComplete', handleNotLoading);
            router.events.off('routeChangeError', handleNotLoading);
        };
    }, [router]);

    return loading;
};

export default useLoading;

Old examples

TypeScript

import { Router } from 'next/router';
import { useState } from 'react';

const useLoading = (): boolean => {
    const [loading, setLoading] = useState<boolean>(false);

    Router.events.on('routeChangeStart', () => {
        setLoading(true);
    });

    Router.events.on('routeChangeComplete', () => {
        setLoading(false);
    });

    Router.events.on('routeChangeError', () => {
        setLoading(false);
    });

    return loading;
};

export default useLoading;

JavaScript

import { Router } from 'next/router';
import { useState } from 'react';

const useLoading = () => {
    const [loading, setLoading] = useState(false);

    Router.events.on('routeChangeStart', () => {
        setLoading(true);
    });

    Router.events.on('routeChangeComplete', () => {
        setLoading(false);
    });

    Router.events.on('routeChangeError', () => {
        setLoading(false);
    });

    return loading;
};

export default useLoading;