patterntypescriptnextjsModerate
useRouter, usePathname, useSearchParams: client-side navigation hooks
Viewed 0 times
next/navigation hooks available from Next.js 13+ App Router
useRouterusePathnameuseSearchParamsuseParamsclient navigationnext/navigation
Error Messages
Problem
Next.js App Router changed the useRouter API — it no longer exposes pathname or query. Code that reads router.pathname or router.query from Pages Router breaks silently in App Router.
Solution
Use the correct hooks for each navigation concern in App Router:
'use client';
import {
useRouter,
usePathname,
useSearchParams,
useParams,
} from 'next/navigation';
export function NavExample() {
const router = useRouter(); // only for imperative navigation
const pathname = usePathname(); // '/blog/hello' — current path
const searchParams = useSearchParams(); // URLSearchParams object
const params = useParams(); // { slug: 'hello' } — dynamic params
const query = searchParams.get('q');
const page = searchParams.get('page') ?? '1';
function navigate() {
router.push('/dashboard'); // navigate programmatically
router.replace('/profile'); // no history entry
router.refresh(); // re-fetch server data
router.back(); // go back
}
// Update search params without full navigation:
function updateSearch(value: string) {
const updated = new URLSearchParams(searchParams.toString());
updated.set('q', value);
router.push(
}
return <div>{pathname}</div>;
}
'use client';
import {
useRouter,
usePathname,
useSearchParams,
useParams,
} from 'next/navigation';
export function NavExample() {
const router = useRouter(); // only for imperative navigation
const pathname = usePathname(); // '/blog/hello' — current path
const searchParams = useSearchParams(); // URLSearchParams object
const params = useParams(); // { slug: 'hello' } — dynamic params
const query = searchParams.get('q');
const page = searchParams.get('page') ?? '1';
function navigate() {
router.push('/dashboard'); // navigate programmatically
router.replace('/profile'); // no history entry
router.refresh(); // re-fetch server data
router.back(); // go back
}
// Update search params without full navigation:
function updateSearch(value: string) {
const updated = new URLSearchParams(searchParams.toString());
updated.set('q', value);
router.push(
${pathname}?${updated.toString()});}
return <div>{pathname}</div>;
}
Why
App Router split the monolithic useRouter from Pages Router into focused hooks. usePathname and useSearchParams are read-only. useRouter is write-only (navigation). This separation makes it clear what triggers re-renders (searchParams changes) vs what doesn't (router navigation).
Gotchas
- useSearchParams must be wrapped in a Suspense boundary — it opts into dynamic rendering
- router.pathname does not exist in App Router — use usePathname() instead
- router.query does not exist in App Router — use useSearchParams() and useParams()
- useRouter from 'next/navigation' is different from 'next/router' — wrong import causes runtime errors
Code Snippets
Reading path and query params in App Router
'use client';
import { usePathname, useSearchParams } from 'next/navigation';
export function Breadcrumbs() {
const pathname = usePathname();
const searchParams = useSearchParams();
const tab = searchParams.get('tab');
return <div>{pathname} - {tab}</div>;
}Context
When handling client-side navigation and reading URL state in Next.js App Router
Revisions (0)
No revisions yet.