Capítulo 65 de 456

Optimizing prefetching

Core Idea

With Cache Components and Partial Prefetching, <Link> prefetches one shared App Shell per route by default. Use prefetch={true} per link to also resolve URL-specific data (searchParams, params) ahead of navigation, and "use cache" / "use cache: private" to include session data in the shell.

Key Concepts

  • App Shell: reusable prefetch payload per route (static output + session UI for routes reading cookies()/headers()); shared across all links to that route.
  • prefetch={true}: resolves a link's URL data (searchParams/params) at prefetch time, on top of the App Shell; costs one server invocation per prefetchable link.
  • prefetch={false} / default: only the App Shell is prefetched; URL-specific content streams in after navigation.
  • partialPrefetching: config flag (requires Cache Components) that enables the App Shell prefetch model.
  • Extract and pass: read cookies()/headers() outside a "use cache" function and pass the value as an argument, so the cache entry is keyed on that value and shared across sessions with the same value.
  • "use cache: private": assigns a cache lifetime to a function that reads runtime request data (cookies/headers) directly inside it; results cached per-browser/session only.

Code Examples

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
}

export default nextConfig
  • O que demonstra: pré-requisito de config para o modelo de prefetch por App Shell.
export default function SearchPage({ searchParams }: PageProps<'/search'>) {
  return (
    <>
      <h1>Search</h1>
      <Suspense fallback={<ResultsSkeleton />}>
        <Results searchParams={searchParams} />
      </Suspense>
    </>
  )
}

async function search(q: string) {
  'use cache'
  return db.search(q)
}
  • O que demonstra: <Link prefetch={true}> para /search?q=react resolve q no prefetch e reaproveita o cache de search(q), eliminando o fallback no clique.
async function getUser() {
  'use cache: private'
  const session = (await cookies()).get('session')?.value
  return db.users.findBySession(session)
}
  • O que demonstra: cache por sessão quando não dá para extrair a leitura de cookies() para fora da função cacheada.

Reference Tables

App ShellPer-link prefetch (prefetch={true})
ScopeUm por rotaUm por <Link prefetch={true}> visível
ContentSaída renderizada da rota menos dado por linkO mesmo, mais dado de URL resolvido
CostLimitado pelo número de rotasLimitado pelo número de links visíveis
RolePrefetch padrãoMais conteúdo pronto antes do clique

Anti-patterns

  • Ativar prefetch={true} em grade de muitos cards: cada link visível dispara uma invocação de servidor; prefira prefetch por hover (intent) nesse caso.
  • Usar prefetch={true} quando o conteúdo precisa ser sempre fresco: o prerender para no mesmo fallback de <Suspense>, então não há ganho.

Key Takeaways

  1. prefetch={true} só ajuda em rotas cuja árvore depende de dados de URL com um lifetime de cache definido ("use cache"/"use cache: private").
  2. Prefetch por link é best-effort: se não completar antes do clique, cai de volta no App Shell.
  3. Dado de sessão (cookies/headers) entra no App Shell via "extract and pass" (compartilhado) ou "use cache: private" (por sessão).
  4. params também precisa de <Suspense>, mesmo quando previsto por generateStaticParams.

Connects To

  • prefetching: comportamento padrão de prefetch e modos de controle (prefetch prop, router.prefetch).
  • preserving-ui-state: Activity/Cache Components trabalham junto ao mesmo modelo de shell + streaming.