Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cloudflare/vinext/llms.txt
Use this file to discover all available pages before exploring further.
The next/navigation module provides hooks and utilities for App Router navigation, including pathname/query access, programmatic navigation, and server-side redirects.
Import
import {
useRouter,
usePathname,
useSearchParams,
useParams,
useSelectedLayoutSegment,
useSelectedLayoutSegments,
redirect,
permanentRedirect,
notFound,
forbidden,
unauthorized,
RedirectType,
} from 'next/navigation'
Client Hooks
useRouter
Returns a router instance for programmatic navigation.
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button onClick={() => router.push('/dashboard')}>
Go to Dashboard
</button>
)
}
Router Methods
push
(href: string, options?: { scroll?: boolean }) => void
Navigate to a new URL (pushes history entry).router.push('/about')
router.push('/blog?filter=tech', { scroll: false })
replace
(href: string, options?: { scroll?: boolean }) => void
Navigate to a new URL (replaces history entry).
Navigate to the previous page.<button onClick={() => router.back()}>Back</button>
Navigate to the next page (if available).<button onClick={() => router.forward()}>Forward</button>
Re-fetch the current page’s RSC stream (soft refresh).// After a mutation
router.refresh()
Manually prefetch a route.<div onMouseEnter={() => router.prefetch('/dashboard')}>
Hover to prefetch
</div>
usePathname
Returns the current pathname (without basePath).
'use client'
import { usePathname } from 'next/navigation'
export default function Nav() {
const pathname = usePathname()
return (
<nav>
<a href="/" className={pathname === '/' ? 'active' : ''}>
Home
</a>
<a href="/about" className={pathname === '/about' ? 'active' : ''}>
About
</a>
</nav>
)
}
Current pathname, e.g., /blog/post-1
- Excludes basePath (if configured)
- Excludes query string and hash
- Updates on navigation
useSearchParams
Returns a URLSearchParams instance for reading query parameters.
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchResults() {
const searchParams = useSearchParams()
const query = searchParams.get('q')
const page = searchParams.get('page') || '1'
return <div>Search results for: {query} (page {page})</div>
}
URLSearchParams instance with methods:
get(key: string): string | null
getAll(key: string): string[]
has(key: string): boolean
entries(): Iterator<[string, string]>
useParams
Returns dynamic route parameters.
'use client'
// File: app/blog/[slug]/page.tsx
import { useParams } from 'next/navigation'
export default function BlogPost() {
const params = useParams<{ slug: string }>()
return <h1>Post: {params.slug}</h1>
}
params
Record<string, string | string[]>
Dynamic route parameters, e.g., { slug: 'hello-world' }
- Single segment:
{ slug: 'post-1' }
- Catch-all:
{ slug: ['2024', '01', 'post'] }
useSelectedLayoutSegment
Returns the active child segment one level below the current layout.
'use client'
// File: app/blog/layout.tsx
import { useSelectedLayoutSegment } from 'next/navigation'
export default function BlogLayout({ children }) {
const segment = useSelectedLayoutSegment()
return (
<div>
<div>Active segment: {segment}</div>
{children}
</div>
)
}
/blog → null (no child)
/blog/post-1 → 'post-1'
/blog/category/tech → 'category'
useSelectedLayoutSegments
Returns all active segments below the current layout.
'use client'
import { useSelectedLayoutSegments } from 'next/navigation'
export default function Layout({ children }) {
const segments = useSelectedLayoutSegments()
return (
<div>
<div>Path: /{segments.join('/')}</div>
{children}
</div>
)
}
/blog → []
/blog/post-1 → ['post-1']
/blog/category/tech → ['category', 'tech']
useServerInsertedHTML
Inject HTML during SSR from client components (CSS-in-JS libraries).
'use client'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleSheetManager } from 'styled-components'
export default function StyledComponentsRegistry({ children }) {
const [sheet] = useState(() => new ServerStyleSheet())
useServerInsertedHTML(() => {
const styles = sheet.getStyleElement()
sheet.instance.clearTag()
return <>{styles}</>
})
return (
<StyleSheetManager sheet={sheet.instance}>
{children}
</StyleSheetManager>
)
}
Server Functions
redirect
Throw a redirect response (caught by the framework).
import { redirect } from 'next/navigation'
export default async function Page() {
const session = await getSession()
if (!session) {
redirect('/login')
}
return <Dashboard />
}
Destination URL (relative or absolute).
type
'push' | 'replace' | RedirectType
default:"'replace'"
Navigation type:
'replace' — 307 redirect (default)
'push' — 307 redirect with history push
RedirectType.push / RedirectType.replace
permanentRedirect
Throw a permanent redirect (308).
import { permanentRedirect } from 'next/navigation'
export default async function Page() {
permanentRedirect('https://example.com')
}
notFound
Throw a 404 Not Found error.
import { notFound } from 'next/navigation'
export default async function Page({ params }) {
const post = await getPost(params.id)
if (!post) {
notFound()
}
return <Post data={post} />
}
Renders the nearest not-found.tsx file.
forbidden
Throw a 403 Forbidden error (Next.js 16+).
import { forbidden } from 'next/navigation'
export default async function AdminPage() {
const user = await getUser()
if (!user.isAdmin) {
forbidden()
}
return <AdminPanel />
}
Renders the nearest forbidden.tsx file (or error.tsx as fallback).
unauthorized
Throw a 401 Unauthorized error (Next.js 16+).
import { unauthorized } from 'next/navigation'
export default async function ProfilePage() {
const session = await getSession()
if (!session) {
unauthorized()
}
return <Profile user={session.user} />
}
Renders the nearest unauthorized.tsx file (or error.tsx as fallback).
Shallow Routing
Update the URL without re-fetching data:
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function Filters() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
function setFilter(filter: string) {
const params = new URLSearchParams(searchParams)
params.set('filter', filter)
router.push(pathname + '?' + params.toString())
}
return (
<button onClick={() => setFilter('tech')}>
Tech Posts
</button>
)
}
Next.js 15+ automatically intercepts history.pushState() / replaceState() calls, so direct manipulation works:
// Also works
window.history.pushState(null, '', '?filter=tech')
Prefetching
Automatic (Link)
<Link> components automatically prefetch on viewport entry (250px margin).
Manual (useRouter)
function Card({ href }) {
const router = useRouter()
return (
<div onMouseEnter={() => router.prefetch(href)}>
<a href={href}>Read more</a>
</div>
)
}
Cache TTL
Prefetched entries are valid for 30 seconds. Stale entries are re-prefetched on next trigger.
basePath Support
All navigation functions automatically handle basePath:
// next.config.js
module.exports = { basePath: '/docs' }
router.push('/api') // Navigates to /docs/api
usePathname() // Returns /api (without basePath)
i18n Support
Use the locale parameter for internationalized routing:
import Link from 'next/link'
<Link href="/" locale="fr">Français</Link>
<Link href="/" locale={false}>Default Locale</Link>
Limitations
SSR hook usage: Client hooks (useRouter, usePathname, etc.) cannot be called during SSR of client components. Use them inside useEffect or event handlers.
Concurrent navigation: Multiple rapid router.push() calls may result in race conditions. Await navigation if order matters.
Source
View source code →
Implementation: /home/daytona/workspace/source/packages/vinext/src/shims/navigation.ts