3

React Server Components vs Client Components: Quando Usar Cada Um no Next.js

O erro que custa kilobytes e latência

No App Router do Next.js, todo componente é Server Component por padrão. Adicionar "use client" no topo de um arquivo parece inofensivo: uma linha, sem erro no terminal, sem warning. O problema aparece depois, quando o bundle do cliente cresce sem necessidade, quando dados que poderiam ser buscados no servidor passam a exigir useEffect + loading state, e quando a árvore de componentes vira uma mistura confusa de responsabilidades.

A distinção entre Server Components (RSC) e Client Components não é cosmética. Ela define onde o código executa, o que vai para o bundle do navegador, e como os dados fluem pela aplicação. Se você está trabalhando com o App Router do Next.js, essa decisão impacta cada rota que você cria.

Como o Next.js decide o que roda onde

O runtime do Next.js separa dois ambientes de execução: o servidor (Node.js ou Edge runtime) e o cliente (navegador). Server Components executam exclusivamente no servidor. O resultado é HTML serializado via um protocolo chamado RSC Payload, que o React no cliente hidrata sem precisar do código-fonte do componente.

Client Components executam nos dois ambientes: primeiro no servidor (para gerar o HTML inicial via SSR), depois no cliente (para hidratar e tornar o componente interativo). O código inteiro do Client Component, incluindo dependências, vai para o bundle JavaScript do navegador.

// app/products/page.tsx
// Server Component por padrão: sem "use client", sem useState, sem useEffect.
// O Next.js executa isso no servidor e envia HTML + RSC Payload.

import { ProductList } from "./product-list";
import { db } from "@/lib/database";

export default async function ProductsPage() {
  // Acesso direto ao banco, sem API intermediária.
  // Isso só funciona porque o código nunca chega ao navegador.
  const products = await db.product.findMany({
    where: { active: true },
    orderBy: { createdAt: "desc" },
    take: 50,
  });

  return (
    <main>
      <h1>Produtos</h1>
      <ProductList products={products} />
    </main>
  );
}
// app/products/product-list.tsx
"use client";

// Este componente precisa de "use client" porque usa useState para filtro local.
// Ele recebe os dados já carregados via props: o fetch aconteceu no servidor.
import { useState } from "react";

type Product = {
  id: string;
  name: string;
  price: number;
  category: string;
};

export function ProductList({ products }: { products: Product[] }) {
  const [filter, setFilter] = useState("");

  const filtered = products.filter((p) =>
    p.name.toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div>
      <input
        type="text"
        placeholder="Filtrar por nome"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
      />
      <ul>
        {filtered.map((product) => (
          <li key={product.id}>
            {product.name} — R$ {product.price.toFixed(2)}
          </li>
        ))}
      </ul>
    </div>
  );
}

A fronteira entre os dois mundos é o "use client". Tudo abaixo dessa diretiva, incluindo componentes filhos importados, entra no bundle do cliente. Tudo acima permanece no servidor.

Matriz de decisão: Server ou Client?

A escolha não depende de gosto. Depende do que o componente faz.

Necessidade do componenteServer ComponentClient Component
Buscar dados de banco/API internaSim (acesso direto, sem fetch extra)Não (exigiria API route + useEffect)
Usar useState, useEffect, useReducerNãoSim
Manipular eventos do DOM (onClick, onChange)NãoSim
Acessar browser APIs (localStorage, geolocation)NãoSim
Renderizar conteúdo estático ou markdownSim (zero JS no cliente)Desnecessário
Usar bibliotecas pesadas de UI (editores, gráficos)Evitar (o peso vai pro bundle)Sim (mas isole o componente)
Acessar variáveis de ambient

Leia o artigo completo em https://www.vivodecodigo.com.br/nextjs/react-server-components-vs-client-components-quando-usar-nextjs

Carregando publicação patrocinada...