nextjs-toploader icon indicating copy to clipboard operation
nextjs-toploader copied to clipboard

Compatibility with useRouter

Open PixeledCode opened this issue 1 year ago • 21 comments

Great Library. Thanks for your work. So far it works great for Link components but for useRouter, it doesn't seem to work at all. Is there some workaround it?

Edit: After going through multiple comments here, I have settled on this approach https://github.com/CivicDataLab/opub-mono/blob/main/apps/www/lib/navigation.tsx

  • I can start and stop the progress bar on demand.
  • Also using zustand to keep a meta state globally for when page transitioning is happening

PixeledCode avatar May 01 '23 13:05 PixeledCode

+1, it will be great. But I think it's impossible now, with current app dir api. I'll be glad if I'm wrong.

Aspedm avatar May 03 '23 15:05 Aspedm

Here is my workaround:

export const usePRouter = () => {
  const router = useRouter();

  const { push } = router;

  router.push = (href, options) => {
    NProgress.start();
    push(href, options);
  };

  return router;
};

// add this to a top level component
const pathname = usePathname();
const searchParams = useSearchParams();

useEffect(() => {
  NProgress.done();
}, [pathname, searchParams]);

zbeyens avatar May 08 '23 16:05 zbeyens

Here is my workaround:

export const usePRouter = () => {
  const router = useRouter();

  const { push } = router;

  router.push = (href, options) => {
    NProgress.start();
    push(href, options);
  };

  return router;
};

// add this to a top level component
const pathname = usePathname();
const searchParams = useSearchParams();

useEffect(() => {
  NProgress.done();
}, [pathname, searchParams]);

are you using this library or the nprogress ? where is that NProgress coming from?

PixeledCode avatar May 09 '23 17:05 PixeledCode

@PixeledCode Forgot to mention I've forked that library

zbeyens avatar May 10 '23 07:05 zbeyens

@PixeledCode Forgot to mention I've forked that library

is your code open? can you share it?

PixeledCode avatar May 10 '23 07:05 PixeledCode

@PixeledCode Nothing different than this repo. You just need to import NProgress to start/stop anywhere you want.

import * as NProgress from 'nprogress';

zbeyens avatar May 10 '23 08:05 zbeyens

It would be great to have this feature! Thank you for your work! :)

Abesoddy avatar May 24 '23 18:05 Abesoddy

@PixeledCode Nothing different than this repo. You just need to import NProgress to start/stop anywhere you want.

import * as NProgress from 'nprogress';

Thanks for this, this approach worked. I am getting this warning, Entire page /[locale]/dashboard/dataset/new deopted into client-side rendering. and since we are using useSearchParams on top level, I guess this warning will stay, right?

PixeledCode avatar May 25 '23 04:05 PixeledCode

import * as NProgress from 'nprogress';

Do you have a workaround with router.refresh() support? most mutations are now just calling refresh to refetch data.

Darren120 avatar May 26 '23 23:05 Darren120

@PixeledCode Nothing different than this repo. You just need to import NProgress to start/stop anywhere you want.

import * as NProgress from 'nprogress';

Thanks for this, this approach worked. I am getting this warning, Entire page /[locale]/dashboard/dataset/new deopted into client-side rendering. and since we are using useSearchParams on top level, I guess this warning will stay, right?

This can be fixed by wrapping around the component by react suspense.

<Suspense fallback={null}><NProgressTop /></Suspense>

mrsafalpiya avatar May 30 '23 09:05 mrsafalpiya

Just put your useRouter in a function and include NProgress.start(); as @zbeyens mentioned.

Add:

import * as NProgress from "nprogress";
import { useRouter } from "next/navigation";

Example usage:

const router = useRouter();
const handlePush = () => {
    NProgress.start();
    router.push("/some-page");
};

nextjs-toploader uses NProgress under the hud.

uixmat avatar Jun 22 '23 12:06 uixmat

This progress bar package handles Next 13, both <Link> and router.push() 👍.

You only have to modify your useRouter imports to import { useRouter } from 'next-nprogress-bar'.

Works well!

dan-pugsley avatar Jul 31 '23 18:07 dan-pugsley

@dan-pugsley awesome!

nodegin avatar Sep 20 '23 10:09 nodegin

https://github.com/TheSGJ/nextjs-toploader/issues/10#issuecomment-1538691096 works like a charm, here's a TypeScript version I'm using:

// useRouter.ts

import { useRouter as useBaseRouter } from "next/navigation";
import NProgress from "nprogress";

export function useRouter() {
  const router = useBaseRouter();

  const { push } = router;

  router.push = async (...args: Parameters<typeof push>) => {
    NProgress.start();
    return push(...args);
  };

  return router;
}

hongaar avatar Nov 14 '23 01:11 hongaar

If you're using the next/router pages router, this might be of help:

import { useRouter } from "next/router";
import NProgress from "nprogress";

export function useCustomRouter(): ReturnType<typeof useRouter> {
  const router = useRouter();

  const originalPush = router.push;

  router.push = (...args: Parameters<typeof router.push>): ReturnType<typeof originalPush> => {
    NProgress.start();
    return originalPush(...args);
  };

  return router;
}

lucas-barake avatar Nov 14 '23 20:11 lucas-barake

#10 (comment) works like a charm, here's a TypeScript version I'm using:

// useRouter.ts

import { useRouter as useBaseRouter } from "next/navigation";
import NProgress from "nprogress";

export function useRouter() {
  const router = useBaseRouter();

  const { push } = router;

  router.push = async (...args: Parameters<typeof push>) => {
    NProgress.start();
    return push(...args);
  };

  return router;
}

Nice solution, but for me it starts loading and never ends

rodrigocipriani avatar Feb 02 '24 19:02 rodrigocipriani

Nice solution, but for me it starts loading and never ends

@rodrigocipriani You will probably need to add some code to make the toploader stop spinning, see: https://github.com/TheSGJ/nextjs-toploader/issues/10#issuecomment-1538691096

hongaar avatar Feb 05 '24 19:02 hongaar

This is not a good solution.

This progress bar package handles Next 13, both <Link> and router.push() 👍.

You only have to modify your useRouter imports to import { useRouter } from 'next-nprogress-bar'.

Works well!

to use this library with Next 13+, you must turn your RootLayout into a client component with 'use client'. this turns your entire app into a client-rendered app, see the docs.

if your server component do any db fetching or use .env vars that are meant to be private (i.e. not prefixed with NEXT_PUBLIC_), then they will break. you won't be able to directly run server-side database queries on a client environment, and your .env vars will be undefined.

EDIT: fixed broken link to Next docs

LuisanSuarez avatar Apr 06 '24 01:04 LuisanSuarez

This is not a good solution.

This progress bar package handles Next 13, both <Link> and router.push() 👍. You only have to modify your useRouter imports to import { useRouter } from 'next-nprogress-bar'. Works well!

to use this library with Next 13+, you must turn your RootLayout into a client component with 'use client'. this turns your entire app into a client-rendered app, see the docs.

if your server component do any db fetching or use .env vars that are meant to be private (i.e. not prefixed with NEXT_PUBLIC_), then they will break. you won't be able to directly run server-side database queries on a client environment, and your .env vars will be undefined.

So you have to treat it like any other provider wrapper. Usually you will create a separate Provider component, add all of the components like this and then wrap layout with it. It's a pretty common pattern

PixeledCode avatar Apr 06 '24 11:04 PixeledCode

This is not a good solution. ... if your server component do any db fetching or use .env vars that are meant to be private (i.e. not prefixed with NEXT_PUBLIC_), then they will break. you won't be able to directly run server-side database queries on a client environment, and your .env vars will be undefined.

So you have to treat it like any other provider wrapper. Usually you will create a separate Provider component, add all of the components like this and then wrap layout with it. It's a pretty common pattern

The Provider pattern is fine too, but you're making a 'use-client' component a parent of all of your app. This turns your entire app into part of the client bundle, and none of it will be server rendered. If you don't care about server rendering it's not an issue. If you do however you can't use next-nprogress-bar.

LuisanSuarez avatar May 13 '24 21:05 LuisanSuarez

You can do this in Holy Loader ↗

'use client';

import { startHolyLoader } from 'holy-loader';

startHolyLoader();
router.push('/your-page')

tomcru avatar May 15 '24 12:05 tomcru