`next/router` to `next/navigation` migration cheatsheet

3 min read
Wrote down the sheet I wish I had.
Post cover imageImage by black-forest-labs/flux-dev

You might find this useful when migrating a Next.js app from the Pages Router to the App Router.

`router` object

Properties of the next/router object and their equivalent using next/navigation exports.

import { useRouter } from 'next/router'

const { pathname, query, asPath } = useRouter()

pathname

import { usePathname } from 'next/navigation'

const pathname = usePathname()

query

import { useSearchParams } from 'next/navigation'

const searchParams = useSearchParams()

const query = Object.fromEntries(searchParams ?? new URLSearchParams())

Note how Object.fromEntries handles search params with multiple values:

Object.fromEntries(new URLSearchParams({ status: ["foo", "bar"]}))
// output: { status: 'foo,bar' }

If instead you need the shape { status: ['foo', 'bar'] } unfortunately you will need to manually assemble the object.

Also, note how arrays are serialized to strings:

new URLSearchParams({ status: ["foo", "bar"]}).toString()
// output: `status=foo%2Cbar`

const sp = new URLSearchParams()
sp.append("status", "foo")
sp.append("status", "bar")
sp.toString()
// output: `status=foo&status=bar`

If you also consumed dynamic params via the query object in the Pages Router, you can merge the object returned by useParams:

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

const searchParams = useSearchParams()
const params = useParams()

const searchParamsRecord = Object.fromEntries(searchParams ?? new URLSearchParams())

const query = { ...searchParamsRecord, ...params }

It's important to note that the App Router will not handle omitting dynamic params if you used to spread existing params while linking or navigating.

import { Link } from 'next/link'

// In the Pages Router, dynamic params will be omitted automatically.
// In the App Router, you have to handle this manually.
<Link href={{
  query: { ...query, foo: "bar" }
}}/>
// This also applies when navigating using router methods e.g. router.push

asPath

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

const pathname = usePathname()
const searchParams = useSearchParams()

const asPath = pathname + "?" + searchParams

Methods

push, replace

import { useRouter } from 'next/router'

const { pathname, query, replace } = useRouter()
replace({ pathname, query: { ...query, foo: "bar" } })
import { useRouter, useSearchParams, usePathname } from 'next/navigation'

const { replace } = useRouter()
const readOnlyParams = useSearchParams()
const pathname = usePathname()

const searchParams = new URLSearchParams(readOnlyParams ?? {})
searchParams.set("foo", "bar")

replace(pathname + "?" + searchParams)

router.events

This is documented by Next.js. Unfortunately, there isn't a direct migration path for every use case and you might have to make drastic changes.

Discuss on Bluesky