Skip to main content
Practice Problems

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 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:

tsx
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:

tsx
// 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

tsx
<Link href={`/docs/${doc.slug}`}> {doc.title} </Link> // With an object <Link href={{ pathname: '/problems', query: { difficulty: 'hard' } }} > Hard problems </Link>

To highlight the current page use usePathname:

tsx
'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:

tsx
'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

MethodDescription
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:

tsx
// 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:

tsx
'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:

tsx
'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

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems