mantine
mantine copied to clipboard
NavigationProgress setup for NextJS13 App Directory
Link to the page where something nasty is located
https://mantine.dev/others/nprogress/#setup-navigationprogress
Exact quote of what is wrong
Hi, I tried setting up NavigationProgress for NextJS13 App Dir but the guide was not available on v7.1.0 docs. and the older v6.0.21 docs still uses the Pages directory setup show here.
// _pages directory_
// components/RouterTransition.tsx
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { NavigationProgress, nprogress } from '@mantine/nprogress';
export function RouterTransition() {
const router = useRouter();
useEffect(() => {
const handleStart = (url: string) => url !== router.asPath && nprogress.start();
const handleComplete = () => nprogress.complete();
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);
};
}, [router.asPath]);
return <NavigationProgress autoReset={true} />;
}
I had a little trouble adapting this to the app directory since next13 app dir uses next/navigation now instead of next/router. I've come up with a way to make this work on the app directory.
It uses usePathname to check the current url and useSearchParams to let you read the URL's query string. This provides a actual start to finish for request.
"use client";
import { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import { NavigationProgress, nprogress } from "@mantine/nprogress";
export function RouterTransition() {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
nprogress.complete();
return () => {
nprogress.start();
};
}, [pathname, searchParams]);
return <NavigationProgress size={5} />;
}
Example of it being used on a handleSubmit function from a form's onSubmit
const handleSubmit = async (values: (typeof form)["values"]) => {
setLoading(true);
nprogress.start();
//DO STUFF HERE
//{
//}
setLoading(false);
nprogress.complete();
};
And being used in the body just like in the old docs.
<body>
<MantineProvider
theme={{
primaryColor: "bright-pink",
colors: {
"bright-pink": [
"#fff4e2",
"#ffe9cc",
"#ffd09c",
"#fdb766",
"#fca13a",
"#fb931d",
"#fc8c0c",
"#e17900",
"#c86a00",
"#ae5a00",
],
},
}}
>
<RouterTransition />
{children}
</MantineProvider>
</body>
I'm kinda new to open source contributing, so I am not particular sure if this serves as a proper Issue.
Thanks,
Hello! I too am interested in this. It looks like this is a problem with Next 13, currently it doesn't supports router events as you pointed. If you are not using Next then it isn't a problem since the component works well with the pure React Router. But for us Next users I think the only solution is to wait for a proper Router in Next.
Help wanted with documentation update
Here's what i am using.
// components/RouterTransition.tsx
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
import { nprogress, NavigationProgress } from '@mantine/nprogress';
export function RouterTransition() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
nprogress.complete();
return () => {
};
}, [pathname, searchParams]);
return <NavigationProgress autoReset={true} />;
}
Here's what i am using.
// components/RouterTransition.tsx import { useEffect } from 'react' import { usePathname, useSearchParams } from 'next/navigation' import { nprogress, NavigationProgress } from '@mantine/nprogress'; export function RouterTransition() { const pathname = usePathname() const searchParams = useSearchParams() useEffect(() => { nprogress.complete(); return () => { }; }, [pathname, searchParams]); return <NavigationProgress autoReset={true} />; }
How does this work for you without you getting an error for 'use client' since you are using useEffect?
This is my implementation, I just added a delay so it actually shows something and it is not immediately gone:
'use client';
import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { NavigationProgress, nprogress } from '@mantine/nprogress';
export function RouterTransition() {
// Get path and params
const pathname = usePathname();
const searchParams = useSearchParams();
// Listen to changes
useEffect(() => {
// Complete after 1s delay
setTimeout(() => nprogress.complete(), 1000);
return () => {
// Start bar
nprogress.start();
};
}, [pathname, searchParams]);
return <NavigationProgress size={5} />;
}
However, I wonder how to implement this before the route changes, so when NextJS is pre-fetching the page, something like YouTube does for example. But this is a start anyway.
EDIT: Well I found some example and I was able to do what I wanted. Here the repo: https://github.com/joulev/nextjs13-appdir-router-events/tree/main
The given solution works fine in dev, but fails during build, due to a Suspense Boundary issue using Next.js V14.1. If anyone is facing that problem, this will do the trick:
'use client'
import { NavigationProgress, nprogress } from '@mantine/nprogress'
import { usePathname } from 'next/navigation'
import { useEffect } from 'react'
export const RouterTransition = () => {
const pathname = usePathname()
useEffect(() => {
nprogress.complete()
return () => {
nprogress.start()
}
}, [pathname])
return <NavigationProgress size={5} />
}