Capítulo 68 de 456

Prefetching

Core Idea

Next.js prefetches routes referenced by <Link> as they enter the viewport, downloading the assets ahead of navigation so client-side transitions feel instant. This page covers the default behavior, its scheduling, the client cache, and how to control it manually.

Key Concepts

  • Automatic prefetch: happens only in production; as each <Link> enters the viewport, Next.js prefetches the route behind it.
  • Static vs dynamic route prefetch (without Cache Components): a static route is prefetched in full; a dynamic route is skipped unless it has a loading.js boundary (then layout-to-fallback is prefetched).
  • Client Cache TTL: controlled by staleTimes.static (default 5 min) and staleTimes.dynamic (off by default) in next.config.js.
  • Prefetch scheduling: task queue prioritizes viewport links, then hover/touch intent links; newer links replace older ones; off-screen links are discarded.
  • Partial Prefetching: with partialPrefetching enabled, <Link> prefetches a per-route App Shell (shared across links to the same route) instead of the full page; rest streams after navigation.
  • router.prefetch(): manual prefetch trigger from useRouter (next/navigation).
  • prefetch prop on <Link>: true (resolve URL data), false (disable), null (restore default behavior).
  • onInvalidate: callback passed to router.prefetch(href, { onInvalidate }), invoked when Next.js suspects cached data is stale.

Code Examples

'use client'
import { useRouter } from 'next/navigation'

export function PricingCard() {
  const router = useRouter()
  return (
    <div onMouseEnter={() => router.prefetch('/pricing')}>
      <CustomLink href="/pricing">View Pricing</CustomLink>
    </div>
  )
}
  • O que demonstra: prefetch manual disparado por hover fora do <Link> padrão.
'use client'
import Link from 'next/link'
import { useState } from 'react'

export function HoverPrefetchLink({ href, children }: { href: string; children: React.ReactNode }) {
  const [active, setActive] = useState(false)
  return (
    <Link href={href} prefetch={active ? null : false} onMouseEnter={() => setActive(true)}>
      {children}
    </Link>
  )
}
  • O que demonstra: adia o prefetch até o usuário mostrar intenção (hover), prefetch={null} restaura o comportamento padrão.
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'

function ManualPrefetchLink({ href, children }: { href: string; children: React.ReactNode }) {
  const router = useRouter()
  useEffect(() => {
    let cancelled = false
    const poll = () => { if (!cancelled) router.prefetch(href, { onInvalidate: poll }) }
    poll()
    return () => { cancelled = true }
  }, [href, router])

  return (
    <a href={href} onClick={(e) => { e.preventDefault(); router.push(href) }}>
      {children}
    </a>
  )
}
  • O que demonstra: recriar o comportamento de prefetch do <Link> nativo com useRouter, incluindo invalidação de cache via onInvalidate.

Reference Tables

Static pageDynamic page
PrefetchedSim, rota inteiraNão, exceto com loading.js
Client Cache TTL5 min (default)Off, salvo se habilitado via staleTimes
Server roundtrip no cliqueNãoSim, streamed após shell
ContextPrefetched payloadClient Cache TTL
Sem loading.jsPágina inteira5 min (staleTimes.static)
Com loading.jsLayout até o primeiro loading boundaryOff por padrão (staleTimes.dynamic)

Anti-patterns

  • Efeitos colaterais (analytics, tracking) direto no corpo de layout/page: rodam durante o prefetch, não na visita real; mova para useEffect ou Server Action disparada por Client Component.
  • Prefetch automático em lista longa de links (infinite scroll): sobrecarrega rede; use prefetch={false} ou hover-triggered prefetch.
  • Estender <Link> sem necessidade: opta o dev por manter manualmente prefetch, invalidação de cache e acessibilidade.

Reference Tables (troubleshooting)

  • Rastreamento de página em layout/page corre durante prefetch → mover para componente Client com useEffect.

Key Takeaways

  1. Prefetch automático só roda em produção, não em next dev.
  2. Sem Cache Components, rotas dinâmicas não são prefetched a menos que tenham loading.js.
  3. Com Partial Prefetching, o modelo muda para um App Shell compartilhado por rota (ver "Optimizing prefetching").
  4. prefetch={false} desliga completamente; hover-triggered prefetch é o meio-termo recomendado para volume alto de links.
  5. Componentes não puros (com side effects) precisam isolar esses efeitos em useEffect/Server Action para não disparar no prefetch.

Connects To

  • optimizing-prefetching: como resolver dados de URL (searchParams/params) no prefetch com prefetch={true} sob Partial Prefetching.
  • preventing-flash: outra técnica ligada a hidratação/navegação client-side.