Dafydd Thomas

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

NextJS Custom Hook

Update 2021-09-28: 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;