next/link and Navigation in Next.js
Next.js provides several ways to navigate: the Link component for declarative navigation, the useRouter hook for programmatic navigation and redirect/permanentRedirect functions for server-side navigation.
Link Component
Link is the primary way to navigate. It renders an <a> tag but intercepts the click and performs client-side navigation without a full page reload:
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<Link href="/docs">Knowledge Base</Link>
<Link href="/problems">Problems</Link>
<Link href="/roadmap">Roadmap</Link>
</nav>
)
}
Prefetching
By default Link prefetches the page when it enters the viewport. This makes transitions instant:
// Prefetch enabled by default
<Link href="/docs/next/ssg">SSG in Next.js</Link>
// Disable prefetch for rarely visited pages
<Link href="/settings" prefetch={false}>Settings</Link>
For static routes prefetch loads the entire RSC Payload. For dynamic routes it loads up to the nearest loading.tsx.
Dynamic href
<Link href={`/docs/${doc.slug}`}>
{doc.title}
</Link>
// With an object
<Link
href={{
pathname: '/problems',
query: { difficulty: 'hard' }
}}
>
Hard problems
</Link>
Active Link
To highlight the current page use usePathname:
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
export function NavLink({
href,
children
}: {
href: string
children: React.ReactNode
}) {
const pathname = usePathname()
const isActive = pathname === href
return (
<Link
href={href}
className={isActive ? 'text-blue-600 font-bold' : 'text-neutral-600'}
>
{children}
</Link>
)
}
useRouter
For programmatic navigation in client components:
'use client'
import { useRouter } from 'next/navigation'
export function LoginButton() {
const router = useRouter()
const handleLogin = async () => {
const result = await login()
if (result.success) {
router.push('/dashboard')
}
}
return <button onClick={handleLogin}>Login</button>
}
useRouter Methods
| Method | Description |
|---|---|
router.push(url) | Navigate to URL (adds to history) |
router.replace(url) | Navigate without adding to history |
router.back() | Go back in history |
router.forward() | Go forward in history |
router.refresh() | Refresh current route (re-fetches data from server) |
router.prefetch(url) | Prefetch a route |
Important:
useRouter from next/navigation (App Router) and useRouter from next/router (Pages Router) are different hooks with different APIs. In the App Router there is no router.query, use useSearchParams instead.
Server-Side Navigation
In server components and Server Actions use redirect:
// In a Server Action
'use server'
import { redirect } from 'next/navigation'
export async function createProblem(formData: FormData) {
const problem = await db.problem.create({ ... })
redirect(`/problems/${problem.id}`)
}
// In a server component
import { redirect } from 'next/navigation'
export default async function Page() {
const user = await getUser()
if (!user) redirect('/auth/login')
return <Dashboard user={user} />
}
useSearchParams
For working with query parameters:
'use client'
import { useSearchParams } from 'next/navigation'
export function FilterPanel() {
const searchParams = useSearchParams()
const difficulty = searchParams.get('difficulty')
return <p>Filter: {difficulty || 'all'}</p>
}
usePathname
To get the current path:
'use client'
import { usePathname } from 'next/navigation'
export function Breadcrumbs() {
const pathname = usePathname()
const segments = pathname.split('/').filter(Boolean)
return (
<nav>
{segments.map((segment, i) => (
<span key={i}>{segment}</span>
))}
</nav>
)
}
Useful Resources
- nextjs.org/docs — Linking and Navigating
- Link API Reference
- useRouter API Reference