JavaScript Avançado

Guia Completo de Next.js Avançado: SSR, SSG, ISR e App Router com Server Components

8 min de leitura

Guia Completo de Next.js Avançado: SSR, SSG, ISR e App Router com Server Components

Renderização no Next.js: Entendendo SSR, SSG e ISR 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. SSR (Server-Side Rendering) executa a renderização no servidor a cada requisição, garantindo conteúdo sempre fresco. Ideal para dados dinâmicos que mudam frequentemente. SSG (Static Site Generation) pré-renderiza páginas em tempo de build, servindo HTML estático — perfeito para conteúdo imutável com altíssima performance. ISR (Incremental Static Regeneration) combina o melhor dos dois mundos: pre-renderiza estaticamente mas regenera em background quando necessário, sem rebuild completo. https://api.example.com/posts/${params.id} App Router e Server Components: A Nova Arquitetura 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. A estrutura usa diretórios para definir rotas: cria a rota .

<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: &#039;1&#039; } }],

fallback: &#039;blocking&#039;, // Gera sob demanda se não existir

};

}

export default function Post({ post }) {

return &lt;h1&gt;{post.title}&lt;/h1&gt;;

}</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 (

&lt;html&gt;

&lt;head&gt;&lt;title&gt;Meu App&lt;/title&gt;&lt;/head&gt;

&lt;body&gt;

&lt;nav&gt;Menu Principal&lt;/nav&gt;

{children}

&lt;/body&gt;

&lt;/html&gt;

);

}

// 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 &lt;article&gt;&lt;h1&gt;{post.title}&lt;/h1&gt;&lt;p&gt;{post.content}&lt;/p&gt;&lt;/article&gt;;

}</code></pre>

<h3>Client Components e Interatividade</h3>

<p>Quando você precisa de estado, event listeners ou hooks do React, use <code>&#039;use client&#039;</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

&#039;use client&#039;;

import { useState } from &#039;react&#039;;

export default function SearchFilter() {

const [query, setQuery] = useState(&#039;&#039;);

return (

&lt;input

type=&quot;text&quot;

value={query}

onChange={(e) =&gt; setQuery(e.target.value)}

placeholder=&quot;Buscar...&quot;

/&gt;

);

}

// app/products/page.js - Server Component que integra o Client Component

import SearchFilter from &#039;@/components/SearchFilter&#039;;

async function fetchProducts() {

const res = await fetch(&#039;https://api.example.com/products&#039;, {

next: { revalidate: 300 },

});

return res.json();

}

export default async function ProductsPage() {

const products = await fetchProducts();

return (

&lt;div&gt;

&lt;SearchFilter /&gt;

&lt;ul&gt;

{products.map(p =&gt; &lt;li key={p.id}&gt;{p.name}&lt;/li&gt;)}

&lt;/ul&gt;

&lt;/div&gt;

);

}</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 &#039;next/server&#039;;

export function middleware(request) {

const token = request.cookies.get(&#039;auth_token&#039;);

if (!token &amp;&amp; request.nextUrl.pathname.startsWith(&#039;/dashboard&#039;)) {

return NextResponse.redirect(new URL(&#039;/login&#039;, request.url));

}

return NextResponse.next();

}

export const config = {

matcher: [&#039;/dashboard/:path&#039;, &#039;/admin/:path&#039;],

};</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 &#039;next/image&#039;;

import dynamic from &#039;next/dynamic&#039;;

import { Suspense } from &#039;react&#039;;

const HeavyChart = dynamic(() =&gt; import(&#039;@/components/Chart&#039;), {

loading: () =&gt; &lt;p&gt;Carregando gráfico...&lt;/p&gt;,

});

export default async function Dashboard() {

return (

&lt;&gt;

&lt;Image src=&quot;/hero.jpg&quot; alt=&quot;Hero&quot; width={1200} height={400} priority /&gt;

&lt;Suspense fallback={&lt;p&gt;Carregando dados...&lt;/p&gt;}&gt;

&lt;DataSection /&gt;

&lt;/Suspense&gt;

&lt;HeavyChart /&gt;

&lt;/&gt;

);

}

async function DataSection() {

const data = await fetch(&#039;https://api.example.com/data&#039;).then(r =&gt; r.json());

return &lt;div&gt;{/ renderiza dados /}&lt;/div&gt;;

}</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>

Comentários

Mais em JavaScript Avançado

Guia Completo de Promises Internamente: Implementando uma Promise do Zero
Guia Completo de Promises Internamente: Implementando uma Promise do Zero

O Que é uma Promise e Por Que Implementar do Zero Uma Promise é um objeto Jav...

Dominando Segurança, Auditoria de Dependências e Hardening Final em Node.js em Projetos Reais
Dominando Segurança, Auditoria de Dependências e Hardening Final em Node.js em Projetos Reais

Segurança em Node.js: Fundamentos e Práticas Essenciais A segurança em aplica...

Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção
Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção

O que é Playwright e Por Que Page Object Model? Playwright é um framework de...