<h2>Renderização no Next.js: Entendendo SSR, SSG e ISR</h2>
<p>A escolha da estratégia de renderização é fundamental para otimizar performance e UX em aplicações modernas. Next.js oferece três abordagens principais que funcionam em complementaridade.</p>
<p><strong>SSR (Server-Side Rendering)</strong> executa a renderização no servidor a cada requisição, garantindo conteúdo sempre fresco. Ideal para dados dinâmicos que mudam frequentemente. <strong>SSG (Static Site Generation)</strong> pré-renderiza páginas em tempo de build, servindo HTML estático — perfeito para conteúdo imutável com altíssima performance. <strong>ISR (Incremental Static Regeneration)</strong> combina o melhor dos dois mundos: pre-renderiza estaticamente mas regenera em background quando necessário, sem rebuild completo.</p>
<pre><code class="language-javascript">// SSG com revalidação (ISR)
export async function getStaticProps({ params }) {
const res = await fetch(https://api.example.com/posts/${params.id});
const post = await res.json();
return {
props: { post },
revalidate: 3600, // Regenera a cada 1 hora
};
}
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }],
fallback: 'blocking', // Gera sob demanda se não existir
};
}
export default function Post({ post }) {
return <h1>{post.title}</h1>;
}</code></pre>
<h2>App Router e Server Components: A Nova Arquitetura</h2>
<p>O App Router (diretor de aplicação) representa a evolução do Next.js, introduzindo uma estrutura baseada em diretórios mais intuitiva e Server Components como padrão. Server Components rodam apenas no servidor, reduzindo JavaScript enviado ao cliente e melhorando segurança — credenciais não são expostas.</p>
<p>A estrutura usa diretórios para definir rotas: <code>app/dashboard/page.js</code> cria a rota <code>/dashboard</code>. Layouts compartilhados são naturais — <code>app/layout.js</code> envolve todas as páginas, e <code>app/dashboard/layout.js</code> envolve apenas páginas internas. Isso elimina re-renders desnecessários em navegação.</p>
<pre><code class="language-javascript">// app/layout.js - Layout raiz (Server Component por padrão)
export default function RootLayout({ children }) {
return (
<html>
<head><title>Meu App</title></head>
<body>
<nav>Menu Principal</nav>
{children}
</body>
</html>
);
}
// app/blog/[id]/page.js - Server Component com dados dinâmicos
async function fetchPost(id) {
const res = await fetch(https://api.example.com/posts/${id}, {
next: { revalidate: 60 }, // ISR automático
});
return res.json();
}
export default async function BlogPost({ params }) {
const post = await fetchPost(params.id);
return <article><h1>{post.title}</h1><p>{post.content}</p></article>;
}</code></pre>
<h3>Client Components e Interatividade</h3>
<p>Quando você precisa de estado, event listeners ou hooks do React, use <code>'use client'</code> no topo do arquivo. Esses componentes rodam no navegador. A estratégia ideal é manter a maioria do app como Server Components e usar Client Components apenas onde necessário — para formulários, filtros, modais.</p>
<pre><code class="language-javascript">// app/components/SearchFilter.js
'use client';
import { useState } from 'react';
export default function SearchFilter() {
const [query, setQuery] = useState('');
return (
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Buscar..."
/>
);
}
// app/products/page.js - Server Component que integra o Client Component
import SearchFilter from '@/components/SearchFilter';
async function fetchProducts() {
const res = await fetch('https://api.example.com/products', {
next: { revalidate: 300 },
});
return res.json();
}
export default async function ProductsPage() {
const products = await fetchProducts();
return (
<div>
<SearchFilter />
<ul>
{products.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
</div>
);
}</code></pre>
<h2>Middleware, Rotas de API e Otimizações Avançadas</h2>
<p>Middleware permite interceptar requisições antes de alcançarem rotas ou páginas, perfeito para autenticação, redirecionamentos e log. Crie um arquivo <code>middleware.js</code> na raiz de <code>src/</code> ou <code>app/</code>.</p>
<pre><code class="language-javascript">// middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
const token = request.cookies.get('auth_token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path', '/admin/:path'],
};</code></pre>
<p>Rotas de API no App Router seguem a mesma filosofia: <code>app/api/posts/route.js</code> cria um endpoint <code>/api/posts</code>. Use exportações nomeadas para cada método HTTP.</p>
<pre><code class="language-javascript">// app/api/posts/route.js
export async function GET(request) {
const posts = await fetchPostsFromDB();
return Response.json(posts);
}
export async function POST(request) {
const data = await request.json();
const newPost = await savePostToDB(data);
return Response.json(newPost, { status: 201 });
}</code></pre>
<p>Para otimização avançada, use <code>Image</code> do Next.js para otimização automática de imagens, <code>dynamic()</code> para lazy loading de componentes pesados, e <code>Suspense</code> para progressive rendering com Server Components. O Image component serve múltiplos formatos, aplica lazy load nativo e responsive images automaticamente.</p>
<pre><code class="language-javascript">import Image from 'next/image';
import dynamic from 'next/dynamic';
import { Suspense } from 'react';
const HeavyChart = dynamic(() => import('@/components/Chart'), {
loading: () => <p>Carregando gráfico...</p>,
});
export default async function Dashboard() {
return (
<>
<Image src="/hero.jpg" alt="Hero" width={1200} height={400} priority />
<Suspense fallback={<p>Carregando dados...</p>}>
<DataSection />
</Suspense>
<HeavyChart />
</>
);
}
async function DataSection() {
const data = await fetch('https://api.example.com/data').then(r => r.json());
return <div>{/ renderiza dados /}</div>;
}</code></pre>
<h2>Conclusão</h2>
<p>Dominar Next.js avançado significa entender que <strong>cada página merece uma estratégia diferente</strong>: use SSG para blogs e documentação, ISR para catálogos que atualizam periodicamente, SSR para dashboards personalizados. <strong>Server Components são o novo padrão</strong> — construa com eles por padrão e use Client Components cirurgicamente apenas para interatividade real. <strong>Otimização é arquitetura</strong>: middleware para segurança, Image para performance, Suspense para UX progressiva — tudo integrado naturalmente no framework.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://nextjs.org/docs/app" target="_blank" rel="noopener noreferrer">Documentação Oficial Next.js - App Router</a></li>
<li><a href="https://nextjs.org/docs/app/building-your-application/rendering/server-components" target="_blank" rel="noopener noreferrer">Server Components - Next.js Docs</a></li>
<li><a href="https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration" target="_blank" rel="noopener noreferrer">ISR e Rendering Strategies</a></li>
<li><a href="https://vercel.com/docs/concepts/next.js/overview" target="_blank" rel="noopener noreferrer">Vercel - Best Practices for Performance</a></li>
<li><a href="https://nextjs.org/conf" target="_blank" rel="noopener noreferrer">Next.js Conf Talks - Advanced Patterns</a></li>
</ul>