How to use the Next.js Router

By

Learn how to use the Next.js App Router useRouter from next/navigation in Next.js 16, with a short note on the older Pages Router API.

~~~

To change routes programmatically in Next.js 16 (App Router), you import useRouter from next/navigation, call it in a Client Component, and then call methods like push() on the router object it returns.

In linking two pages in Next.js using Link we saw how to use the Link component to declaratively handle routing in Next.js apps.

It’s really handy to manage routing in JSX, but sometimes you need to trigger a routing change programmatically. Think of what happens after a form submission: there’s no link to click, your code decides where to go next.

Here’s an example of accessing the App Router:

'use client'

import { useRouter } from 'next/navigation'

export default function LoginButton() {
  const router = useRouter()
  //...
}

useRouter from next/navigation only works in Client Components, so the file needs 'use client' at the top (or the hook must live in a client child). This is the client side router, so call its methods from event handlers or inside useEffect(), not while the component renders. Client Components are pre-rendered on the server too, and there’s no browser to navigate there.

The ones you’ll likely use the most are push() and prefetch().

push() allows us to programmatically trigger a URL change, in the frontend:

router.push('/login')

Pass a full path string when you need query parameters:

router.push('/search?term=nextjs')

If you don’t want the current page to stay in the browser history, use replace() instead. It works like push(), but the back button skips the replaced page. That’s what you want after a login redirect, for example:

router.replace('/dashboard')

Prefetching a URL

prefetch() allows us to programmatically prefetch a URL, useful when we don’t have a Link tag which automatically handles prefetching for us:

router.prefetch('/login')

Full example:

'use client'

import { useEffect } from 'react'
import { useRouter } from 'next/navigation'

export default function PrefetchLogin() {
  const router = useRouter()

  useEffect(() => {
    router.prefetch('/login')
  }, [router])
}

Note that prefetching only happens in production builds. In development it’s a no-op, so don’t be surprised if you see no network activity there.

Reading the current route

In the App Router, the current path and search params are separate hooks, not fields on useRouter():

'use client'

import { usePathname, useSearchParams } from 'next/navigation'

export default function SearchTerm() {
  const pathname = usePathname()
  const searchParams = useSearchParams()
  const term = searchParams.get('term')
  // ...
}

useSearchParams() has one catch. On a statically rendered page, the component that calls it must sit inside a <Suspense> boundary, otherwise next build fails with an error about a missing Suspense boundary. Wrap <SearchTerm /> in <Suspense fallback={null}> where you render it.

Pages Router (older apps)

If your app still lives under pages/, import from next/router instead:

import { useRouter } from 'next/router'

const router = useRouter()
router.push('/login')
router.push({ pathname: '/search', query: { term: 'nextjs' } })

That API still has router.pathname, router.query, router.isReady, and router.events. Be careful with router.query on statically generated pages: during the first render it’s an empty object, and it gets filled right after hydration, so check router.isReady before reading it in an effect.

New Next.js 16 apps should use the App Router and next/navigation. For setup from scratch, see how to install Next.js.

Tagged: Next.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about next: