~/TechPurAI
~/tutorials/nextjs-from-scratch/next-link-and-navigation
intermediate·part 12 of 22·3 min read

next/link and client-side navigation

Updated Aug 16, 2026JavaScript · Next.js

Link has appeared in every part of this series that links between routes, without ever covering what it does beyond rendering an <a> tag. This part is that explanation — prefetching specifically, since it's the part that isn't visible just from reading the code.

What happens on hover

tsx
import Link from "next/link";

<Link href="/posts/hello-app-router">Read this post</Link>

By default, Link prefetches the linked route's data in the background as soon as it scrolls into the viewport (and again, more eagerly, when it's actually hovered or focused) — by the time a click actually happens, the page it's navigating to has often already been fetched. Combined with client-side routing (no full page reload — the browser doesn't re-request the whole document, just swaps in the new route's content), this is what makes App Router navigation feel close to instant for a route that's already been prefetched.

Turning prefetching off

tsx
<Link href="/posts/hello-app-router" prefetch={false}>
  Read this post
</Link>

Prefetching every link on a very large listing page — hundreds of posts — means hundreds of background requests most visitors will never actually click through to. prefetch={false} opts a specific link out, worth doing deliberately on a page with an unusually large number of links rather than as a default habit.

When a plain anchor tag is still correct

tsx
<a href="https://github.com/vercel/next.js" target="_blank" rel="noopener noreferrer">
  Next.js on GitHub
</a>

Link is built for internal navigation within the same Next.js app — prefetching and client-side routing only make sense for routes the app itself controls. An external link, like the one above, gets no benefit from Link at all and should stay a plain <a> tag; using Link for it doesn't break anything, it just adds nothing.

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 ? "active" : undefined}>
      {children}
    </Link>
  );
}

usePathname() only works in a Client Component — it's a hook reading live browser navigation state, the same category of client-only API useState was in part 3 — which is why this needs its own small "use client" component rather than living directly in the (Server Component) root layout from part 4.

Common mistake

Reaching for router.push() inside a click handler to navigate somewhere a plain Link would work fine. Link gets prefetching and is crawlable by search engines (a real href in the rendered HTML) for free — an imperative router.push() call is the right tool for navigating after something happens (a successful form submission, covered starting part 14), not as a substitute for a link a person can see and click directly.

The blog's reading experience and SEO layer are both solid now. Next: giving the blog a real backend operation — a Route Handler, Next.js's way of building an actual API endpoint.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous11. next/image: automatic image optimizationnext →13. Route Handlers: building an API endpoint