Home Quizzes Leaderboard Competitions Learn Hire Us
About Contact
Log In Sign Up
Learn Next.js Navigation & Middleware

Navigation & Middleware

⏱ 15 min read read
next/link --- Client-Side Navigation:

import Link from 'next/link';

<Link href="/about">About</Link>

<Link href="/blog/hello-world">Read Post</Link>

<Link href={{ pathname: '/blog/[slug]', query: { slug: 'hello'
} }}>

Dynamic

</Link>

// Open in new tab

<Link href="https://example.com" target="\_blank"
rel="noopener">

External

</Link>

// Replace history instead of push

<Link href="/" replace>Home</Link>

// Prefetch (default: true for visible links)

<Link href="/heavy-page" prefetch={false}>No prefetch</Link>

useRouter --- Programmatic Navigation:

'use client';

import { useRouter, usePathname, useSearchParams } from
'next/navigation';

const router = useRouter();

const pathname = usePathname(); // current path: '/about'

const searchParams = useSearchParams(); // URLSearchParams object

router.push('/dashboard'); // navigate

router.replace('/login'); // replace (no back button)

router.back(); // go back

router.refresh(); // re-fetch server data for current route

Active Link Styling:

'use client';

import Link from 'next/link';

import { usePathname } from 'next/navigation';

function NavLink({ href, label }: { href: string; label: string }) {

const pathname = usePathname();

const isActive = pathname === href;

return (

<Link href={href}

className={isActive ? 'text-blue-600 font-bold' : 'text-gray-600
hover:text-gray-900'}

>

{label}

</Link>

);

}

Middleware --- Runs Before Every Request:

// middleware.ts (root)

import { NextRequest, NextResponse } from 'next/server';

export function middleware(request: NextRequest) {

const token = request.cookies.get('token')?.value;

if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {

return NextResponse.redirect(new URL('/login', request.url));

}

return NextResponse.next();

}

export const config = { matcher: ['/dashboard/:path*',
'/api/:path*'] };
Code Example
'use client';

import { useRouter, usePathname, useSearchParams } from
'next/navigation';

import Link from 'next/link';

import { useCallback } from 'react';

const NAV = [

{ href: '/', label: 'Home' },

{ href: '/courses', label: 'Courses' },

{ href: '/dashboard', label: 'Dashboard' },

{ href: '/about', label: 'About' },

];

function NavBar() {

const pathname = usePathname();

return (

<nav className="flex gap-6 px-6 py-4 border-b">

{NAV.map(({ href, label }) => {

const active = pathname === href;

return (

<Link

key={href}

href={href}

className={active

? 'text-blue-600 font-semibold border-b-2 border-blue-600 pb-1'

: 'text-gray-500 hover:text-gray-900 transition'}

>

{label}

</Link>

);

})}

</nav>

);

}

// Pagination with URL search params

function Pagination({ total, perPage }: { total: number; perPage:
number }) {

const router = useRouter();

const searchParams = useSearchParams();

const currentPage = Number(searchParams.get('page') ?? '1');

const totalPages = Math.ceil(total / perPage);

const goTo = useCallback((page: number) => {

const params = new URLSearchParams(searchParams.toString());

params.set('page', String(page));

router.push(`?${params.toString()}`);

}, [router, searchParams]);

return (

<div className="flex gap-2">

<button onClick={() => goTo(currentPage - 1)} disabled={currentPage
=== 1}>←</button>

<span>Page {currentPage} of {totalPages}</span>

<button onClick={() => goTo(currentPage + 1)} disabled={currentPage
=== totalPages}>→</button>

</div>

);

}
← Images, Fonts & Static Assets State Management --- Zustand & React Que →

Log in to track your progress and earn badges as you complete lessons.

Log In to Track Progress