Skip to the solution
HardFrontend10 min read

How does prefetching and navigation caching work in Next.js 16?

122 views

The problem

Question presented to candidate: "Our product list page has fifty <Link>s to product pages. Clicking one feels instant in production but not in development, and the network tab shows a lot of requests. Explain what Next.js 16 prefetches, where it keeps it, and what Partial Prefetching changed in 16.3 and 16.4."

What a strong answer should cover:

  • <Link> prefetches in production only, when the link enters the viewport (and again on hover if expired), through a scheduler that prioritises visible and hovered links.
  • The prefetched RSC payload is stored in the in-memory client cache keyed by route segment; shared layouts are fetched once (16.0 layout deduplication and incremental prefetching), and static data is reused for about 5 minutes by default.
  • Without Partial Prefetching: static routes are prefetched fully, dynamic routes only down to the nearest loading.js.
  • With partialPrefetching: true (16.3, requires cacheComponents), a default link loads one reusable App Shell per route; prefetch={true} adds the URL-specific content; the rest renders on navigation.
  • 16.4 added prefetch() and navigation() from next/cache to defer subtrees to later navigation stages, and ensureStatic to enforce that a stage stays static.

Clarifying questions expected:

  • "Is Cache Components enabled?" β€” Partial Prefetching and the new functions require it.
  • "Are the pages static, cached or per-user?" β€” that decides what can be in the shell and what must wait.

Code / implementation expected: Optional β€” a link list, a page using prefetch()/navigation(), and the config.

  • nextjs
  • prefetching
  • routing
  • performance
  • next-16

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

10 min read Β· 8 sections

Read the code

A page that defers work to later navigation stages (16.4)
import { Suspense } from "react";
import { cacheLife, navigation, prefetch } from "next/cache";

export const ensureStatic = "shell"; // the App Shell must never need a request-time render

async function getPost(slug: string) {
  "use cache";
  cacheLife("hours");
  return { slug, title: `Post ${slug}` };
}

async function getRelated(slug: string) {
  "use cache";
  cacheLife("hours");
  await new Promise((r) => setTimeout(r, 200)); // expensive ranking
  return [`${slug}-a`, `${slug}-b`];
}

async function Body({ params }: { params: Promise<{ slug: string }> }) {
  await prefetch(); // out of the shared App Shell; a prefetch={true} link or the click renders it
  const { slug } = await params;
  const post = await getPost(slug);
  return <h2 id="title">{post.title}</h2>;
}

async function Related({ params }: { params: Promise<{ slug: string }> }) {
  await navigation(); // only when the user actually navigates
  const { slug } = await params;
  const items = await getRelated(slug);
  return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>;
}

export default function Page({ params }: PageProps<"/post/[slug]">) {
  return (
    <main>
      <h1>Blog</h1>
      <Suspense fallback={<p>loading post...</p>}>
        <Body params={params} />
      </Suspense>
      <Suspense fallback={<p>loading related...</p>}>
        <Related params={params} />
      </Suspense>
    </main>
  );
}
Links: default App Shell, per-link prefetch, and hover-triggered
"use client";
import Link from "next/link";
import { useState } from "react";

// With partialPrefetching: "auto" loads the shared App Shell, true adds URL-specific content
export function HoverPrefetchLink({ href, children }: { href: string; children: React.ReactNode }) {
  const [active, setActive] = useState(false);
  return (
    <Link href={href} prefetch={active ? true : "auto"} onMouseEnter={() => setActive(true)}>
      {children}
    </Link>
  );
}
next.config.ts for Partial Prefetching
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  cacheComponents: true, // required by partialPrefetching
  partialPrefetching: true, // 16.3+: one App Shell per route as the default prefetch
};

export default nextConfig;

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 123 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextHard

How do you stream responses and Server-Sent Events from a Route Handler?

Continue