Next.js Link and Navigation
Use the Link component and useRouter for fast client-side navigation in Next.js.
TL;DR
- 01Use Link for client-side navigation instead of <a> tags.
- 02Prefetch future pages automatically when links enter the viewport.
- 03Use useRouter hook to navigate programmatically after user actions.
Tips
- 01Always use Link for internal navigation — it enables better performance and a smoother user experience.
Warnings
- 01Avoid using <a> tags for internal links as they cause full page reloads and lose state.
Link Component
- Use Link for client-side navigation without page reload.
import Link from "next/link"; export default function Navigation() { return ( <nav> <Link href="/">Home</Link> <Link href="/about">About</Link> <Link href="/blog">Blog</Link> </nav> ); } - Link enables faster navigation and better performance.
- Only the needed data is fetched, not the whole page.
- Uses client-side rendering for instant transitions.
- Pass custom className or style props directly to Link.
<Link href="/contact" className="nav-link">Contact</Link>
Dynamic Links
- Create links with dynamic parameters.
import Link from "next/link"; export default function PostList({ posts }) { return ( <ul> {posts.map(post => ( <li key={post.id}> <Link href={`/blog/${post.slug}`}> {post.title} </Link> </li> ))} </ul> ); } - Use href with objects for typed routes.
<Link href={{ pathname: "/blog/[slug]", query: { slug: post.slug } }} > {post.title} </Link>
Prefetching
- Prefetch pages to load them in the background.
<Link href="/about" prefetch={true}> About </Link> - Prefetching is enabled by default for links in viewport.
- Disable prefetching for expensive operations.
<Link href="/api/heavy-data" prefetch={false}> Load Data </Link> - Manually prefetch with router.prefetch().
import { useRouter } from "next/navigation"; const router = useRouter(); function prefetchAbout() { router.prefetch("/about"); }
Programmatic Navigation
- Use useRouter for navigating with code.
import { useRouter } from "next/navigation"; export default function LoginForm() { const router = useRouter(); async function handleSubmit(e) { e.preventDefault(); // authenticate... router.push("/dashboard"); } return <form onSubmit={handleSubmit}>...</form>; } - Router methods: push, replace, back, forward.
router.push("/new-page"); // Add to history router.replace("/new-page"); // Replace history router.back(); // Go back router.forward(); // Go forward
Active Links
- Highlight current page in navigation.
"use client"; import { usePathname } from "next/navigation"; import Link from "next/link"; export default function Navigation() { const pathname = usePathname(); return ( <nav> <Link href="/" className={pathname === "/" ? "active" : ""} > Home </Link> <Link href="/about" className={pathname === "/about" ? "active" : ""} > About </Link> </nav> ); } - Use usePathname to get the current route.
- Compare pathname to set active classes.
FAQ
Use the useRouter hook from next/navigation and call router.push('/destination') after your async logic completes. For replacing history instead of pushing, use router.replace('/destination') to prevent users from navigating back.
Pass the interpolated path directly to Link's href: <Link href={/products/${product.id}}>. For complex routes, you can also pass an object: href={{ pathname: '/products/[id]', query: { id: product.id } }}.
Use the usePathname hook from next/navigation to get the current path, then compare it to the link's href to conditionally apply an active CSS class or style.
In production, Next.js prefetches linked pages when the Link enters the viewport, which speeds up navigation. You can disable this per-link with the prefetch={false} prop if the destination is rarely visited or costly to prefetch.