Introdução ao Next.js: SSR e SSG
1. O que é Next.js e por que ele existe?
O ecossistema JavaScript evoluiu significativamente desde o surgimento do React em 2013. As Single Page Applications (SPAs) tradicionais, embora poderosas para interatividade no cliente, apresentavam limitações críticas: baixo desempenho em SEO (motores de busca não executavam JavaScript) e tempo de carregamento inicial lento (o navegador precisava baixar todo o bundle JavaScript antes de renderizar qualquer conteúdo).
Next.js, criado pela Vercel em 2016, surgiu como uma solução full-stack para React que resolve esses problemas ao oferecer renderização tanto no servidor quanto no cliente. Ele se baseia em três pilares fundamentais:
- Páginas baseadas em arquivos: cada arquivo na pasta
pages/torna-se automaticamente uma rota - Renderização híbrida: SSR (Server-Side Rendering), SSG (Static Site Generation) e CSR (Client-Side Rendering) na mesma aplicação
- Ecossistema Node.js: roda inteiramente sobre Node.js, permitindo acesso a APIs do servidor, bancos de dados e sistemas de arquivos
2. Renderização do lado do servidor (SSR) no Next.js
No SSR, quando um usuário faz uma requisição, o servidor Node.js executa o código React, gera o HTML completo e o envia ao navegador. Isso significa que o conteúdo já está pronto quando o JavaScript chega ao cliente.
Implementação prática com getServerSideProps:
// pages/produtos/[id].js
export default function Produto({ produto }) {
return (
<div>
<h1>{produto.nome}</h1>
<p>{produto.descricao}</p>
<span>Preço: R$ {produto.preco}</span>
</div>
);
}
export async function getServerSideProps(context) {
const { id } = context.params;
const res = await fetch(`https://api.exemplo.com/produtos/${id}`);
const produto = await res.json();
return {
props: { produto },
};
}
Vantagens:
- SEO completo: motores de busca veem o HTML final
- Dados sempre atualizados em cada requisição
- Ideal para páginas com conteúdo dinâmico e personalizado
Desvantagens:
- Maior tempo de resposta no servidor (TTFB mais alto)
- Custo computacional por requisição
- Necessidade de infraestrutura de servidor dedicada
3. Geração de sites estáticos (SSG) no Next.js
SSG pré-renderiza as páginas durante o tempo de build, gerando arquivos HTML estáticos que podem ser servidos via CDN. Isso oferece performance excepcional, pois não há processamento no servidor para cada requisição.
Implementação prática com getStaticProps:
// pages/blog.js
export default function Blog({ posts }) {
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.titulo}</li>
))}
</ul>
);
}
export async function getStaticProps() {
const res = await fetch('https://api.exemplo.com/posts');
const posts = await res.json();
return {
props: { posts },
revalidate: 3600, // Revalida a cada 1 hora (ISR)
};
}
Diferenças chave entre SSG e SSR:
| Característica | SSG | SSR |
|---|---|---|
| Momento da renderização | Build | Cada requisição |
| Performance | Excelente (CDN) | Variável (servidor) |
| Dados atualizados | Requer rebuild/revalidação | Sempre atualizados |
| Ideal para | Blogs, documentação, landing pages | Dashboards, e-commerce, dados personalizados |
4. Rotas dinâmicas e getStaticPaths
Para páginas SSG com parâmetros dinâmicos, usamos getStaticPaths para definir quais caminhos serão pré-renderizados.
// pages/posts/[slug].js
export default function Post({ post }) {
return (
<article>
<h1>{post.titulo}</h1>
<div dangerouslySetInnerHTML={{ __html: post.conteudo }} />
</article>
);
}
export async function getStaticPaths() {
const res = await fetch('https://api.exemplo.com/posts');
const posts = await res.json();
const paths = posts.map(post => ({
params: { slug: post.slug },
}));
return { paths, fallback: 'blocking' };
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.exemplo.com/posts/${params.slug}`);
const post = await res.json();
return {
props: { post },
revalidate: 60,
};
}
O fallback: 'blocking' permite que páginas não pré-renderizadas sejam geradas sob demanda na primeira requisição, combinando SSG com geração incremental (ISR).
5. Data fetching híbrido: combinando SSR, SSG e CSR
Estratégias mistas permitem otimizar cada parte da página conforme sua necessidade:
// pages/perfil.js - SSG para dados estáticos + CSR para dados dinâmicos
import useSWR from 'swr';
export default function Perfil({ usuario }) {
// Dados estáticos (SSG)
const { data: comentarios, error } = useSWR(
`/api/comentarios/${usuario.id}`,
fetcher
);
if (error) return <div>Erro ao carregar comentários</div>;
if (!comentarios) return <div>Carregando...</div>;
return (
<div>
<h1>{usuario.nome}</h1>
<p>Bio: {usuario.bio}</p>
<h2>Comentários recentes:</h2>
<ul>
{comentarios.map(com => (
<li key={com.id}>{com.texto}</li>
))}
</ul>
</div>
);
}
export async function getStaticProps() {
const res = await fetch('https://api.exemplo.com/usuario/1');
const usuario = await res.json();
return {
props: { usuario },
revalidate: 86400, // Revalida a cada 24h
};
}
Neste exemplo, o perfil do usuário é estático (SSG), enquanto os comentários são carregados via CSR com SWR, garantindo dados sempre frescos sem sacrificar a performance inicial.
6. Performance e boas práticas com SSR e SSG
Otimizações essenciais:
// pages/dashboard.js - SSR com lazy loading
import dynamic from 'next/dynamic';
const GraficoPesado = dynamic(() => import('../components/Grafico'), {
loading: () => <p>Carregando gráfico...</p>,
ssr: false, // Não renderiza no servidor
});
export default function Dashboard({ dados }) {
return (
<div>
<h1>Dashboard</h1>
<GraficoPesado dados={dados} />
</div>
);
}
export async function getServerSideProps() {
const res = await fetch('https://api.exemplo.com/dashboard');
const dados = await res.json();
return {
props: { dados },
};
}
Comparação de métricas:
| Métrica | SSR | SSG | SPA Puro |
|---|---|---|---|
| TTFB | Médio (100-500ms) | Baixo (<50ms) | Baixo |
| FCP | Rápido | Rápido | Lento |
| LCP | Médio | Rápido | Lento |
| SEO | Excelente | Excelente | Ruim |
7. Deploy e considerações finais
Checklist para escolher a estratégia de renderização:
- SSG: Conteúdo que raramente muda (blog, documentação, landing pages)
- SSR: Dados personalizados por usuário (dashboards, e-commerce com preços dinâmicos)
- CSR: Funcionalidades pós-carregamento (comentários, chat, formulários complexos)
- ISR: Conteúdo que muda periodicamente (notícias, catálogos de produtos)
Comandos para deploy:
# Build para produção
next build
# Iniciar servidor Node.js (para SSR)
next start
# Gerar exportação estática (apenas SSG)
next export
Opções de deploy:
- Vercel: Suporte nativo, deploy automático via Git
- Netlify: Suporte a SSG com adaptações
- Servidor Node.js próprio: Controle total sobre SSR
Next.js oferece flexibilidade incomparável ao permitir escolher a estratégia ideal para cada página, combinando o melhor dos mundos estático e dinâmico.
Referências
- Documentação oficial do Next.js sobre Data Fetching — Guia completo sobre getStaticProps, getServerSideProps e ISR
- Next.js: Static vs Server Rendering (Vercel Blog) — Artigo técnico comparando SSR e SSG com métricas reais
- SWR: React Hooks for Data Fetching — Biblioteca recomendada para CSR híbrido em Next.js
- Next.js: Incremental Static Regeneration (Vercel Docs) — Tutorial prático sobre ISR e fallback
- Next.js: Dynamic Routes — Documentação oficial sobre rotas dinâmicas e getStaticPaths
- Next.js vs Gatsby vs Create React App (LogRocket) — Comparação detalhada entre frameworks React
- Next.js: Performance Optimization Guide — Guia oficial de otimização de performance com lazy loading e code splitting