Skip to content

Instantly share code, notes, and snippets.

@jaydenseric
Last active November 29, 2023 11:34
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jaydenseric/15a61ecfe3b52599409787c33fcfe9da to your computer and use it in GitHub Desktop.
Save jaydenseric/15a61ecfe3b52599409787c33fcfe9da to your computer and use it in GitHub Desktop.
A route change indicator for Next.js using React hooks.
import classNameProp from 'class-name-prop';
import { useRouter } from 'next/router';
import React from 'react';
import styles from './RouteIndicator.module.css';
const DONE_DURATION = 250;
export default function RouteIndicator() {
const router = useRouter();
const [loading, setLoading] = React.useState(null);
const [timeoutId, setTimeoutId] = React.useState(null);
const onRouteChangeStart = React.useCallback(() => {
setLoading(true);
}, []);
const onRouteChangeDone = React.useCallback(() => {
setLoading(false);
setTimeoutId(
setTimeout(() => {
setTimeoutId(null);
setLoading(null);
}, DONE_DURATION)
);
}, []);
React.useEffect(() => {
router.events.on('routeChangeStart', onRouteChangeStart);
router.events.on('routeChangeComplete', onRouteChangeDone);
router.events.on('routeChangeError', onRouteChangeDone);
return () => {
router.events.off('routeChangeStart', onRouteChangeStart);
router.events.off('routeChangeComplete', onRouteChangeDone);
router.events.off('routeChangeError', onRouteChangeDone);
};
}, [onRouteChangeDone, onRouteChangeStart, router.events]);
React.useEffect(
() => () => {
if (timeoutId) clearTimeout(timeoutId);
},
[timeoutId]
);
return (
<div
className={classNameProp(
styles.indicator,
loading !== null && (loading ? styles.loading : styles.done)
)}
style={{ '--RouteIndicator-done-duration': `${DONE_DURATION}ms` }}
/>
);
}
.indicator {
position: fixed;
left: 0;
top: 0;
right: 100%;
z-index: 2;
height: 2px;
background-color: hsl(207, 100%, 38%);
box-shadow: 0 1px 8px hsla(0, 0%, 0%, 0.12);
opacity: 0;
transition-property: right, opacity;
transition-duration: 0s;
pointer-events: none;
}
.loading {
right: 5%;
opacity: 0.95;
transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
transition-duration: 8s, 0s;
}
.done {
right: 0;
transition-duration: var(--RouteIndicator-done-duration);
transition-delay: 0s, var(--RouteIndicator-done-duration);
}
@prakhar-parashar-au7
Copy link

Heyy, I'm using next.js, this code works fine when I use it in [path].js , and when route is being changed from '/something' to '/somethingelse',
but it doesn't work when I put it in index.js, and when I'm going from '/' to '/somethingElse'. Is there any reason why it doesn't work in index.js?

@jaydenseric
Copy link
Author

@prakhar-parashar-au7 I'm not exactly sure what was causing your problem, but I just updated this Gist to reflect how I do things these days (except I swapped a few device-agnostic-ui CSS variables for hardcoded color values as not everyone uses that UI library). Probably the new version works better.

@naveen-bharathi
Copy link

Heyy, I'm using next.js, this code works fine when I use it in [path].js , and when route is being changed from '/something' to '/somethingelse',
but it doesn't work when I put it in index.js, and when I'm going from '/' to '/somethingElse'. Is there any reason why it doesn't work in index.js?

@prakhar-parashar-au7 You must write this inside your _app.js file. Only then it'll work in all the pages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment