# React Server Components em produção: padrões e armadilhas > React Server Components em produção: padrões testados em batalha, anti-padrões comuns e estratégias de depuração para aplicações Next.js 15 robustas. - Published: 2026-03-29 - Updated: 2026-05-04 - Author: Anthony Fillion-Maillet - Tags: react server components, next.js 15, rsc patterns, production, react 19 - Reading time: 9 min --- Os React Server Components (RSC) mudam de forma fundamental como funciona a renderização no servidor no Next.js 15, mas a adoção em produção revela armadilhas que a documentação oficial nem sempre cobre. Este artigo destrincha os padrões que funcionam, os que quebram e como diagnosticar problemas antes que cheguem à produção. > **Server Components e Client Components** > > Um Server Component roda exclusivamente no servidor e envia zero JavaScript ao navegador. Um Client Component (marcado com `"use client"`) roda dos dois lados. A regra: manter os Client Components o menores e mais abaixo possível na árvore. ## A fronteira servidor-cliente: entender o padrão de boundary A armadilha mais comum com RSC envolve a fronteira entre Server e Client Components. Assim que um componente carrega a diretiva `"use client"`, **todos os filhos importados também viram Client Components**, mesmo sem a diretiva. ```tsx // ProductPage.tsx (Server Component) import { ProductDetails } from './ProductDetails' import { AddToCartButton } from './AddToCartButton' export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params const product = await getProduct(id) return (
{/* Server Component: acesso direto ao banco */} {/* Client Component: interatividade isolada */}
) } ``` ```tsx // AddToCartButton.tsx (Client Component) 'use client' import { useState } from 'react' export function AddToCartButton({ productId, price }: { productId: string; price: number }) { const [adding, setAdding] = useState(false) async function handleAdd() { setAdding(true) await fetch('/api/cart', { method: 'POST', body: JSON.stringify({ productId, quantity: 1 }), }) setAdding(false) } return ( ) } ``` O padrão-chave: passar dados como **props serializáveis** do Server Component para o Client Component. Funções, classes e objetos Date não atravessam essa fronteira. ## Anti-padrão: o wrapper Client Component desnecessário Um erro frequente é criar um Client Component que envolve filhos Server Components, forçando toda a subárvore para o lado cliente. ```tsx // PageWrapper.tsx — ANTI-PADRÃO 'use client' import { useState } from 'react' // Todo o conteúdo filho vai para o lado cliente export function PageWrapper({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState('light') return (
{children}
) } ``` > **children como slot** > > A solução: passar Server Components como `children` (padrão slot). Filhos passados como props continuam sendo Server Components mesmo quando o pai é um Client Component. O código acima funciona corretamente desde que `children` venha de um pai Server Component. ```tsx // layout.tsx (Server Component) import { PageWrapper } from './PageWrapper' import { HeavyServerContent } from './HeavyServerContent' export default function Layout() { return ( {/* Continua Server Component apesar do wrapper cliente */} ) } ``` Esse padrão de composição preserva os benefícios da renderização no servidor para conteúdo pesado, ao mesmo tempo em que habilita interatividade no nível do wrapper. ## Manuseio de dados assíncronos: o padrão fetch no componente React 19 e Next.js 15 suportam `async/await` diretamente em Server Components. Esse padrão simplifica a busca de dados em comparação com a antiga abordagem `getServerSideProps`. ```tsx // UserProfile.tsx (Server Component) import { cache } from 'react' // Deduplica chamadas idênticas dentro do mesmo render const getUser = cache(async (userId: string) => { const res = await fetch(`https://api.example.com/users/${userId}`, { next: { revalidate: 3600 }, // Cache por 1 hora }) if (!res.ok) throw new Error('User not found') return res.json() }) export default async function UserProfile({ userId }: { userId: string }) { const user = await getUser(userId) return (

{user.name}

{user.email}

Membro desde {new Date(user.createdAt).toLocaleDateString('pt-BR')}

) } ``` Três pontos críticos: - O `cache()` do React deduplica chamadas idênticas durante um único render do servidor - `next: { revalidate }` controla a duração do cache do lado do Next.js - Erros num Server Component assíncrono acionam o `error.tsx` mais próximo ## Armadilha de serialização: o que não atravessa a fronteira Dados trocados entre Server e Client Components precisam ser serializáveis em JSON. Veja o que provoca erros silenciosos ou crashes. ```tsx // ARMADILHA: passar tipos não serializáveis // Função — não funciona { /* server action */ }} /> // Usar uma Server Action importada no lugar import { submitForm } from '@/lib/actions/form' // Objeto Date — não funciona // String ISO — funciona // Map, Set, RegExp — não funciona // Objeto plano ou array — funciona ``` As Server Actions (funções marcadas com `"use server"`) são a exceção: podem ser passadas como props para um Client Component porque o Next.js as transforma em endpoints HTTP. ## Streaming e Suspense: padrões de carregamento progressivo O streaming SSR com Suspense envia HTML ao navegador de forma progressiva. O padrão ótimo usa boundaries Suspense granulares ao redor de cada seção assíncrona. ```tsx // DashboardPage.tsx (Server Component) import { Suspense } from 'react' import { RevenueChart } from './RevenueChart' import { RecentOrders } from './RecentOrders' import { UserStats } from './UserStats' export default function DashboardPage() { return (
}> }> }>
) } ``` Cada seção carrega de forma independente. Se `RevenueChart` leva 3 segundos e `UserStats` 200 ms, as estatísticas aparecem instantaneamente sem esperar o gráfico. > **Suspense e SEO** > > O conteúdo dentro de um boundary Suspense é renderizado no servidor e incluído no HTML inicial. Crawlers veem o conteúdo completo. O streaming afeta apenas a velocidade de entrega ao navegador, não a visibilidade SEO. ## Depuração em produção: rastrear problemas RSC Erros RSC costumam ser crípticos. Três técnicas de diagnóstico funcionam em produção. **1. Identificar incompatibilidades de hidratação** ```tsx // debug-hydration.tsx 'use client' import { useEffect, useState } from 'react' export function HydrationDebug() { const [isClient, setIsClient] = useState(false) useEffect(() => { setIsClient(true) }, []) if (process.env.NODE_ENV !== 'development') return null return (
{isClient ? 'Client' : 'Server'}
) } ``` **2. Registrar o payload RSC** No Next.js 15, ativar o logging RSC em `next.config.ts`: ```typescript // next.config.ts const nextConfig = { logging: { fetches: { fullUrl: true, // Mostra as URLs completas dos fetch }, }, } export default nextConfig ``` **3. Verificar o tamanho do payload** Um payload RSC superdimensionado (> 128 KB) degrada o desempenho. Monitorar requisições de rede com o content type `text/x-component` no DevTools. ## Padrão avançado: composição com Server Actions Server Actions combinadas com Server Components criam um padrão CQRS natural: leituras no servidor (RSC), escritas via actions. ```tsx // TodoList.tsx (Server Component) import { getTodos } from '@/lib/services/todo' import { TodoForm } from './TodoForm' import { deleteTodo } from '@/lib/actions/todo' export default async function TodoList() { const todos = await getTodos() return (
    {todos.map(todo => (
  • {todo.title}
  • ))}
) } ``` ```tsx // actions/todo.ts 'use server' import { revalidatePath } from 'next/cache' import { TodoService } from '@/lib/services/todo' export async function deleteTodo(formData: FormData) { const id = formData.get('id') as string await TodoService.delete(id) revalidatePath('/todos') } ``` A chamada a `revalidatePath` dispara um novo render do Server Component com dados atualizados, sem recarregar a página inteira. Para uma preparação de entrevista mais profunda sobre esses tópicos, confira o módulo [Next.js Server Actions](/technologies/react-next/interview-questions/nextjs-server-actions) e o módulo [Next.js Data Fetching](/technologies/react-next/interview-questions/nextjs-data-fetching) no SharpSkill. A [documentação oficial do React](https://react.dev/reference/rsc/server-components) cobre toda a especificação dos Server Components. ## Conclusão - Manter Client Components pequenos e isolados na parte de baixo da árvore de componentes - Usar o padrão slot (`children`) para preservar Server Components dentro de um wrapper cliente - Sempre verificar a serializabilidade das props que atravessam a fronteira servidor-cliente - Colocar boundaries Suspense granulares ao redor de cada seção assíncrona independente - Monitorar o tamanho do payload RSC em produção (alvo < 128 KB) - Combinar Server Components (leituras) e Server Actions (escritas) para um padrão CQRS natural - Usar o `cache()` do React para deduplicar requisições dentro de um único render do servidor --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/react-next/react-server-components-patterns-pitfalls