mantine icon indicating copy to clipboard operation
mantine copied to clipboard

NavigationProgress setup for NextJS13 App Directory

Open dinogomez opened this issue 2 years ago • 6 comments

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,

dinogomez avatar Sep 29 '23 21:09 dinogomez

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.

mateuscqueiros avatar Sep 30 '23 02:09 mateuscqueiros

Help wanted with documentation update

rtivital avatar Oct 01 '23 11:10 rtivital

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} />;
}

smyja avatar Oct 13 '23 08:10 smyja

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?

pshivvy avatar Nov 03 '23 03:11 pshivvy

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

pzehle avatar Nov 04 '23 15:11 pzehle

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} />
}


JulianDM1995 avatar Jan 29 '24 15:01 JulianDM1995