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*'] };
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*'] };
Log in to track your progress and earn badges as you complete lessons.
Log In to Track Progress