Renderização Estática vs. Dinâmica
Entenda quando o Next.js renderiza rotas no momento da compilação (estático) versus no momento da requisição (dinâmico) e como controlar isso.
Busque em todas as páginas da documentação
Entenda quando o Next.js renderiza rotas no momento da compilação (estático) versus no momento da requisição (dinâmico) e como controlar isso.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida -- pronto para copiar e colar.
// Estático por padrão -- sem funções dinâmicas, sem fetches não cacheados
export default async function AboutPage() {
return <h1>Sobre Nós</h1>; // Compilado no momento da implantação
}
// Torna-se dinâmico automaticamente ao usar funções dinâmicas
import { cookies } from "next/headers";
export default async function DashboardPage() {
const cookieStore = await cookies(); // Isso opta pela renderização dinâmica
return <h1>Painel</h1>;
}
// Força estático ou dinâmico com configuração de segmento
export const dynamic = "force-dynamic"; // Sempre renderizar no momento da requisição
export const dynamic = "force-static"; // Sempre renderizar no momento da compilação
// Pré-renderiza rotas dinâmicas no momento da compilação
export async function generateStaticParams() {
const posts = await fetchAllPosts();
return posts.map((p) => ({ slug: p.slug }));
}Quando usar isso: Você precisa entender por que uma rota é estática ou dinâmica, ou deseja controlar explicitamente o comportamento de renderização.
// app/blog/[slug]/page.tsx -- Geração estática com generateStaticParams
import { notFound } from "next/navigation";
type Post = { slug: string; title: string; content: string; date: string };
async function getPost(slug: string): Promise<Post | null> {
const res = await fetch(`https://api.example.com/posts/${slug}`, {
next: { tags: [`post-${slug}`] },
});
if (!res.ok) return null;
return res.json();
}
// Pré-renderiza esses caminhos no momento da compilação
export async function generateStaticParams() {
const res = await fetch("https://api.example.com/posts");
const posts: Post[] = await res.json();
return posts.map((post) => ({
slug: post.slug,
}));
}
// Gera metadados para cada post
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return { title: "Não Encontrado" };
return { title: post.title };
}
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound();
return (
<article className="prose max-w-2xl mx-auto p-6">
<h1>{post.title}</h1>
<time className="text-gray-500">{post.date}</time>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}// app/dashboard/page.tsx -- Renderização dinâmica (usa cookies)
import { cookies } from "next/headers";
import { db } from "@/lib/db";
import { verifySession } from "@/lib/auth";
export default async function DashboardPage() {
const cookieStore = await cookies();
const token = cookieStore.get("session")?.value;
const session = await verifySession(token);
if (!session) {
return <p>Por favor, faça login</p>;
}
const stats = await db.userStats.findUnique({
where: { userId: session.userId },
});
return (
<main className="p-6">
<h1 className="text-2xl font-bold">Bem-vindo, {session.user.name}</h1>
<div className="grid grid-cols-3 gap-4 mt-6">
<StatCard label="Pedidos" value={stats?.orders ?? 0} />
<StatCard label="Receita" value={`$${stats?.revenue ?? 0}`} />
<StatCard label="Clientes" value={stats?.customers ?? 0} />
</div>
</main>
);
}
function StatCard({ label, value }: { label: string; value: string | number }) {
return (
<div className="border rounded p-4">
<p className="text-sm text-gray-500">{label}</p>
<p className="text-2xl font-bold">{value}</p>
</div>
);
}O que isso demonstra:
generateStaticParams.cookies().cookies(), headers(), searchParams)cache: "no-store" em nenhum fetchdynamic = "force-dynamic"cookies(), headers(), searchParams (a prop da página), useSearchParams() (sem Suspense) e connection().generateStaticParams informa ao Next.js quais segmentos de rota dinâmicos pré-renderizar no momento da compilação. Caminhos não retornados por esta função são renderizados sob demanda (e cacheados) ou retornam 404, dependendo de dynamicParams.revalidate para reconstruir páginas estáticas em segundo plano após um intervalo de tempo. A primeira requisição após o intervalo recebe a página desatualizada; requisições subsequentes recebem a versão atualizada.Controlando o comportamento para caminhos não em generateStaticParams:
// Permite a renderização de caminhos não pré-gerados (comportamento padrão)
export const dynamicParams = true;
// Retorna 404 para caminhos não pré-gerados
export const dynamicParams = false;Revalidação em nível de segmento:
// Revalida todas as páginas sob este layout a cada 60 segundos
export const revalidate = 60;
// Nunca revalida (totalmente estático até a próxima implantação)
export const revalidate = false;Misturando estático e dinâmico na mesma página com Suspense:
import { Suspense } from "react";
import { cookies } from "next/headers";
// O shell estático é renderizado no momento da compilação
export default function Page() {
return (
<main>
<h1>Página do Produto</h1> {/* Estático */}
<StaticProductInfo /> {/* Estático */}
<Suspense fallback={<p>Carregando carrinho...</p>}>
<DynamicCartPreview /> {/* Dinâmico -- chama cookies() */}
</Suspense>
</main>
);
}
async function DynamicCartPreview() {
const cookieStore = await cookies();
const cartId = cookieStore.get("cart-id")?.value;
// ...
}// Tipo de retorno de generateStaticParams
export async function generateStaticParams(): Promise<{ slug: string }[]> {
// ...
}
// Para rotas dinâmicas aninhadas
export async function generateStaticParams(): Promise<{
category: string;
slug: string;
}[]> {
// ...
}
// Tipos de configuração de segmento
export const dynamic: "auto" | "force-dynamic" | "force-static" | "error" = "auto";
export const dynamicParams: boolean = true;
export const revalidate: number | false = false;
export const runtime: "nodejs" | "edge" = "nodejs";Uma única chamada cookies() torna toda a rota dinâmica -- Mesmo que 99% da página seja conteúdo estático, uma função dinâmica opta toda a rota para renderização dinâmica. Correção: Use Partial Prerendering (PPR) ou isole a parte dinâmica em um limite Suspense.
generateStaticParams é executado apenas no momento da compilação -- Novo conteúdo adicionado após a compilação não é pré-renderizado até a próxima compilação ou a menos que dynamicParams seja true (que renderiza sob demanda). Correção: Mantenha dynamicParams: true (o padrão) para que novos caminhos sejam renderizados na primeira requisição e depois cacheados.
force-static com funções dinâmicas gera um erro -- Se você exportar dynamic = "force-static" mas a página chamar cookies(), a compilação falha. Correção: Remova a função dinâmica ou mude para dynamic = "auto".
revalidate: 0 não significa "revalidar imediatamente" -- Significa "sempre dinâmico". Correção: Use um número positivo para o comportamento ISR.
Páginas estáticas com busca de dados no lado do cliente -- Uma página pode ser gerada estaticamente, mas ainda buscar dados no cliente com SWR ou React Query. O HTML estático serve como o shell. Correção: Este é um padrão válido, não um bug -- apenas esteja ciente do estado inicial vazio.
| Abordagem | Use Quando | Não Use Quando |
|---|---|---|
| Geração Estática (padrão) | Conteúdo muda com pouca frequência, pode ser compilado no momento da implantação | Dados são específicos do usuário ou em tempo real |
ISR (revalidate: N) | Conteúdo muda periodicamente, você quer velocidade do CDN com frescor | Dados precisam estar frescos em cada requisição |
| Renderização Dinâmica | Dados são específicos do usuário, usa cookies ou headers | Conteúdo é o mesmo para todos os usuários |
| Partial Prerendering (PPR) | Você quer um shell estático com "buracos" dinâmicos | A página inteira é totalmente estática ou totalmente dinâmica |
| Busca no lado do cliente | Você precisa de atualizações em tempo real após o carregamento da página | Dados podem ser buscados inteiramente no servidor |
force-static | Você quer garantir que uma rota nunca seja dinâmica | A rota genuinamente precisa de dados no momento da requisição |
Uma rota é estática por padrão. Ela se torna dinâmica se:
cookies(), headers(), searchParams, connection())cache: "no-store" em um fetchdynamic = "force-dynamic"generateStaticParams informa ao Next.js quais segmentos de rota dinâmicos pré-renderizar no momento da compilação.dynamicParams for true) ou retornam 404.revalidate: N): páginas são reconstruídas em segundo plano após N segundos. A primeira requisição após o intervalo recebe a página desatualizada; requisições subsequentes recebem a versão atualizada.<Suspense> para que apenas essa porção seja dinâmica.A compilação falhará com um erro. Você não pode forçar uma rota a ser estática quando ela usa funções dinâmicas. Remova a função dinâmica ou mude para dynamic = "auto".
Significa sempre renderizar dinamicamente (equivalente a force-dynamic). Não significa "revalidar imediatamente". Use um número positivo (por exemplo, revalidate: 60) para o comportamento ISR.
Sim. Uma página pode ser gerada estaticamente, mas ainda buscar dados no cliente usando SWR ou React Query. O HTML estático serve como o shell, e os dados do lado do cliente preenchem após a hidratação.
// Permite renderização sob demanda para caminhos desconhecidos (padrão)
export const dynamicParams = true;
// Retorna 404 para caminhos desconhecidos
export const dynamicParams = false;export async function generateStaticParams(): Promise<
{ slug: string }[]
> {
const posts = await fetchAllPosts();
return posts.map((p) => ({ slug: p.slug }));
}Para rotas dinâmicas aninhadas, inclua todos os parâmetros de segmento no tipo de retorno.
export const dynamic: "auto" | "force-dynamic" | "force-static" | "error" = "auto";
export const dynamicParams: boolean = true;
export const revalidate: number | false = false;
export const runtime: "nodejs" | "edge" = "nodejs";Envolva a parte dinâmica em um limite <Suspense>. O shell estático é renderizado no momento da compilação, e o componente dinâmico (por exemplo, um que chama cookies()) é transmitido no momento da requisição.
Revisado por Chris St. John·Última atualização: 7 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥