Routing, links and navigation
The Link component and prefetching, the navigation hooks, route groups and catch-all segments, and when to redirect.
Link and prefetching
import Link from 'next/link'
export default function Nav() {
return (
<nav>
<Link href="/guides">Guides</Link>
<Link href="/guides/intro" scroll={false}>Intro (keep scroll position)</Link>
<Link href={{ pathname: '/guides/[slug]', params: { slug: 'intro' } }}>Typed href</Link>
<Link href="/dashboard" replace>Replace the history entry</Link>
<Link href="/api/export" prefetch={false}>Download</Link>
</nav>
)
}Linkrenders an anchor and performs a client-side navigation: the new route's tree replaces the old one without reloading the document.- In production, links in the viewport are prefetched, so the target's code is usually already downloaded when the user clicks. Prefetching is off in development.
- A dynamic route needs the concrete path (
/guides/intro), not the folder pattern with brackets. scroll={false}is the right choice for filters and tabs;replaceis for a navigation the back button should skip.- Use a plain anchor for external URLs, downloads and mail links. A plain anchor to an internal route also works, but it costs a full document request.
useRouter, usePathname and useSearchParams
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function Filters() {
const router = useRouter()
const pathname = usePathname() // '/guides', no query string
const params = useSearchParams() // read-only URLSearchParams
const tag = params.get('tag') ?? 'all'
function setTag(next) {
const usp = new URLSearchParams(params)
next === 'all' ? usp.delete('tag') : usp.set('tag', next)
router.push(pathname + '?' + usp.toString(), { scroll: false })
}
return <button onClick={() => setTag('vue')}>Vue ({tag})</button>
}- In the App Router these hooks come from
next/navigation.next/routerstill exists for the Pages Router, and mixing the two imports is a frequent mistake. useSearchParamsis read-only. To change the query you build a new URL and push or replace it.router.refresh()re-renders the server components for the current route — the way to pick up fresh data after a mutation without a full reload.- Only client components can use the hooks. In a server component you read the same values from the
paramsandsearchParamsprops instead. usePathnameexcludes the query string. Compare a trimmed path for active links, and be careful withstartsWithso/guides-newdoes not light up/guides.
💡
Everything in
next/navigation is client-only. A server component already receives the same values as props — params and searchParams — so read them there instead of shipping a client boundary just to know which page is rendering.Route groups, catch-all segments and redirects
app/
(marketing)/
about/page.tsx -> /about (group name is not in the URL)
pricing/page.tsx -> /pricing
(shop)/
layout.tsx -> shell shared by every route in the group
docs/[...slug]/page.tsx -> /docs/a/b catch-all
shop/[[...filters]]/page.tsx -> /shop and /shop/a optional catch-all
old/page.tsx -> redirect('/new')- Parentheses group routes without adding a segment, which is how one site gets a marketing shell and an app shell with different layouts.
- A catch-all segment matches one or more levels and arrives as an array; the optional form also matches the bare parent path.
redirect()fromnext/navigationworks by throwing a control-flow signal, so never call it inside atryblock that swallows exceptions.- Use
notFound()for a missing resource and a config redirect or middleware for URL moves that must apply before anything renders. - Nesting is about layout and data scope, not about the URL: a folder with a layout and no page is a legitimate structure.
FAQ
Why is nothing prefetched in development?
Prefetching is disabled in development so a stale cache does not hide your changes. Measure navigation behaviour against a production build, where visible links are prefetched and navigation is usually instant.
Link or a plain anchor?
Use
Link for in-app routes so navigation stays client-side and prefetching applies. Use an anchor for external destinations, file downloads and anything that must leave the app.Related
Setting up a project with create-next-app Middleware, headers, cookies and sessions
Last refreshed 2026-09-18.