Capítulo 5 de 456
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.
<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).loading.tsx (rota inteira) ou <Suspense> (granular).<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.// app/dashboard/loading.tsx
export default function Loading() {
return <LoadingSkeleton />
}
// 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 }))
}
// 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>
)
}
// 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>
</>
)
}
window.history.pushState com o router do Next.js via usePathname/useSearchParams.| Causa de lentidão | Solução |
|---|---|
Rota dinâmica sem loading.tsx | adicionar loading.tsx para partial prefetch e navegação imediata |
Dynamic segment sem generateStaticParams | adicionar generateStaticParams para prerender em build time |
| Rede lenta, prefetch não termina | useLinkStatus para feedback imediato (com debounce de ~100ms) |
| Hidratação não concluída | reduzir bundle JS (@next/bundle-analyzer), mover lógica para o servidor |
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.loading.tsx habilita partial prefetch em rotas dinâmicas; <Suspense> dá controle granular sobre o que estica.generateStaticParams, um dynamic segment cai em dynamic rendering mesmo que pudesse ser prerenderizado.useLinkStatus cobre o caso de rede lenta onde nem o prefetch nem o fallback chegam a tempo.window.history.pushState/replaceState sincronizam com usePathname/useSearchParams do Next.js, permitindo mudar a URL sem re-renderizar via navegação completa.<Suspense> e streaming aparecem de novo, aplicados a dados em vez de rotas inteiras.