Skip to content

Docs: How to migrate from async router.push to new next/navigation push method #61737

Description

@ajwootto

What is the improvement or update you wish to see?

The new router in next/navigation no longer returns a promise from the push method. From what I've been able to understand, this is so that it can work with the startTransition feature of React, but the Next docs don't contain any information about this or how to successfully replace any functionality that was relying on "awaiting" a router.push call

Is there any context that might help us understand?

The docs for the push method are currently one sentence here:
https://nextjs.org/docs/app/api-reference/functions/use-router#userouter

The migration guide does not mention the fact that the method is no longer async or describe how a migration should take place to the new push
https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-5-migrating-routing-hooks

This is the one place I've been able to find mention of how it can be used with useTransition, although it also doesn't seem to trigger the pending state in many cases (maybe it just doesn't work in page router?)
#49810

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-5-migrating-routing-hooks

Activity

  1. Guo-dalu commented on Feb 7, 2024

    @Guo-dalu

    Here here, also need it badly.

  2. arcticfly commented on May 22, 2024

    @arcticfly

    Agreed. We built our own hacky solution to await router pushes, but would really like built-in support for this!

  3. ArianHamdi commented on May 22, 2024

    @ArianHamdi
    Contributor

    Agreed. We built our own hacky solution to await router pushes, but would really like built-in support for this!

    Can you please share your workaround

  4. arcticfly commented on May 22, 2024

    @arcticfly

    Agreed. We built our own hacky solution to await router pushes, but would really like built-in support for this!

    Can you please share your workaround

    // modify router so that it awaits the route change before returning
    export const useAsyncRouter = () => {
      const router = useRouter();
      const pathname = usePathname();
      const [resolveRouteChanged, setResolveRouteChanged] = useState<(() => void) | null>(null);
    
      useEffect(() => {
        resolveRouteChanged?.();
      }, [pathname]);
    
      const asyncRouter = useMemo(() => {
        const push = async <RouteType>(href: NextRouteTypes.RouteImpl<RouteType>) => {
          router.push(href as any);
    
          await new Promise<void>((resolve) => {
            // wait for the route change to complete
            setResolveRouteChanged(resolve);
          });
        };
    
        return { ...router, push };
      }, [router]);
    
      return asyncRouter;
    };
    
  5. Cauen commented on Jul 24, 2024

    @Cauen

    Here are an alternative implementation of https://stackoverflow.com/a/77931487/6638583 workaround code

    'use client'
    import { useRouter } from 'next/navigation'
    import { useEffect, useTransition } from 'react'
    
    // Define the type for the observer callback function
    type ObserverCallback = () => void
    
    const createRouteObserver = () => {
      let observer: ObserverCallback | null = null
    
      const setObserver = (callback: ObserverCallback) => {
        observer = callback
      }
    
      const notify = () => {
        if (observer) {
          observer()
        }
      }
    
      return { setObserver, notify }
    }
    
    const routeObserver = createRouteObserver()
    
    export const useAsyncRoutePush = () => {
      const [isPending, startTransition] = useTransition()
      const router = useRouter()
    
      const asynPush = async (path: string) => {
        return new Promise<void>((resolve) => {
          startTransition(() => {
            router.push(path)
          })
    
          routeObserver.setObserver(() => {
            resolve()
          })
        })
      }
    
      useEffect(() => {
        if (!isPending) {
          routeObserver.notify()
        }
      }, [isPending])
    
      return asynPush
    }

    Usage:

    export default function MyComponent() {
      const asyncPush = useAsyncRoutePush()
    
      return <Button onClick={async () => {
        await asyncPush('/')
        console.log("Route changed")
      }}>
        Go / route
      </Button>
    }
  6. allicanseenow commented on Sep 12, 2024

    @allicanseenow

    This should be mentioned in the documentation as a breaking change as there's no built-in support for this feature now.

  7. TradeeHubDev commented on Nov 12, 2024

    @TradeeHubDev

    This feature is really needed because I have an MUI grid which I added custom url tracking of the page in the url via push and when user clicks so fast since it's async all of it that means the router.push is not being awaited and clicking too fast is causing a crash look below

    image

  8. removed
    DocumentationRelated to Next.js' official documentation.
    on Jan 22, 2025
  9. HasanMothaffar commented on Feb 19, 2025

    @HasanMothaffar

    +1 This feature is really needed. It's been a year with no response? I don't know why the Next.js team prioritizes RSCs and server stuff while neglecting important features like these.

  10. mhanzelka commented on Sep 17, 2025

    @mhanzelka

    Still nothing? Is something what is keeping it back from implementing this in app router?

  11. lucastnr commented on Oct 2, 2025

    @lucastnr

    I would love it implemented too, it's a bummer that we have to do it manually

  12. daveidivide commented on Oct 9, 2025

    @daveidivide

    Same here, not enjoying writing hacks to get around the fact that router.push isn't async

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions