Capítulo 5 de 456

Linking and Navigating

Core Idea

Como o Next.js mantém a navegação rápida apesar de renderizar rotas no servidor por padrão: prefetching, streaming e client-side transitions, mais o que fazer quando a navegação parece lenta.

Key Concepts

  • Prerendering: server rendering que acontece em build time ou revalidação, resultado fica em cache.
  • Dynamic Rendering: server rendering que acontece em request time, em resposta a uma requisição do cliente.
  • Prefetching: carregar uma rota em background antes do usuário navegar; <Link> faz isso automaticamente ao entrar no viewport ou hover. Rota estática é prefetchada por completo; rota dinâmica é pulada ou parcialmente prefetchada (se houver loading.tsx).
  • Streaming: servidor envia partes de uma rota dinâmica assim que ficam prontas, em vez de esperar tudo renderizar; habilitado via loading.tsx (rota inteira) ou <Suspense> (granular).
  • Client-side transitions: <Link> evita reload completo, mantém layouts compartilhados e troca o conteúdo dinamicamente.
  • loading.tsx: Next.js envolve automaticamente o page.tsx correspondente numa <Suspense> boundary; layout que acessa dados dinâmicos/não cacheados (cookies(), headers(), fetch não cacheado) bloqueia a navegação em vez de cair no loading.js do mesmo segmento.
  • generateStaticParams: sem ela, um dynamic segment que poderia ser prerenderizado cai em dynamic rendering em request time.
  • useLinkStatus: hook que expõe pending para dar feedback visual imediato em redes lentas, quando o prefetch não termina antes do clique.
  • prefetch={false}: desativa o prefetch de um <Link>; útil em listas grandes (ex.: infinite scroll) para economizar recursos.
  • prefetch={active ? null : false}: padrão de prefetch-on-hover, ativado via onMouseEnter.

Code Examples

// app/dashboard/loading.tsx
export default function Loading() {
  return <LoadingSkeleton />
}
  • O que demonstra: streaming de rota inteira via arquivo de convenção.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch('https://.../posts').then((res) => res.json())
  return posts.map((post) => ({ slug: post.slug }))
}
  • O que demonstra: prerender de dynamic segments em build time para habilitar prefetch completo.
// app/ui/hover-prefetch-link.tsx
'use client'
import Link from 'next/link'
import { useState } from 'react'

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: reduzir uso de recursos limitando prefetch a rotas que o usuário provavelmente vai visitar.
// app/ui/sort-products.tsx
'use client'
import { useSearchParams } from 'next/navigation'

export default function SortProducts() {
  const searchParams = useSearchParams()
  function updateSorting(sortOrder: string) {
    const params = new URLSearchParams(searchParams.toString())
    params.set('sort', sortOrder)
    window.history.pushState(null, '', `?${params.toString()}`)
  }
  return (
    <>
      <button onClick={() => updateSorting('asc')}>Sort Ascending</button>
      <button onClick={() => updateSorting('desc')}>Sort Descending</button>
    </>
  )
}
  • O que demonstra: integração de window.history.pushState com o router do Next.js via usePathname/useSearchParams.

Reference Tables

Causa de lentidãoSolução
Rota dinâmica sem loading.tsxadicionar loading.tsx para partial prefetch e navegação imediata
Dynamic segment sem generateStaticParamsadicionar generateStaticParams para prerender em build time
Rede lenta, prefetch não terminauseLinkStatus para feedback imediato (com debounce de ~100ms)
Hidratação não concluídareduzir bundle JS (@next/bundle-analyzer), mover lógica para o servidor

Anti-patterns

  • Desativar prefetch globalmente "por segurança": rota estática só busca no clique, rota dinâmica precisa renderizar no servidor antes de navegar, ambos pioram a percepção de velocidade.
  • Confiar em loading.js para layout que lê cookies()/headers(): esse layout bloqueia a navegação em vez de mostrar o fallback; a correção é isolar o acesso dinâmico em <Suspense> próprio ou mover para page.js.

Key Takeaways

  1. Prefetch + streaming + client-side transitions juntos fazem apps server-rendered parecerem client-rendered.
  2. loading.tsx habilita partial prefetch em rotas dinâmicas; <Suspense> dá controle granular sobre o que estica.
  3. Sem generateStaticParams, um dynamic segment cai em dynamic rendering mesmo que pudesse ser prerenderizado.
  4. useLinkStatus cobre o caso de rede lenta onde nem o prefetch nem o fallback chegam a tempo.
  5. window.history.pushState/replaceState sincronizam com usePathname/useSearchParams do Next.js, permitindo mudar a URL sem re-renderizar via navegação completa.

Connects To

  • Server and Client Components: streaming e RSC Payload são a base técnica do prefetch/navegação descritos aqui.
  • Fetching Data: <Suspense> e streaming aparecem de novo, aplicados a dados em vez de rotas inteiras.