mantinedev / mantine

A fully featured React components library
https://mantine.dev
MIT License
26.35k stars 1.87k forks source link

NavigationProgress setup for NextJS13 App Directory #4927

Closed dinogomez closed 2 months ago

dinogomez commented 1 year ago

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,

mateuscqueiros commented 1 year ago

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.

rtivital commented 1 year ago

Help wanted with documentation update

smyja commented 11 months ago

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} />;
}
pshivvy commented 11 months ago

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?

pzehle commented 11 months ago

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

JulianDM1995 commented 8 months ago

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