Files
pulse-libs/libs/react/NEXTJS_BEST_PRACTICES.md
Pulse ae39e45460 feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS:
- next-best-practices      v0.1.0  (CLEAN) — Next.js App Router, RSC, caching, data
- nextjs-patterns          v1.0.0  (CLEAN) — Next.js 15: Server Actions, route handlers
- vite                     v1.0.0  (CLEAN) — env vars, aliases, proxy, CJS compat
- uncle-bob                v1.0.0  (CLEAN) — Clean Code, SOLID, Clean Architecture
- clean-code-review        v1.0.0  (CLEAN) — naming, guard clauses, anti-patterns, refactoring
- vue                      v1.0.0  (CLEAN) — Vue framework
- vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity

BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos):
- typescript/ — TS safe + generics gotchas
- react/ — Next.js App Router + Vite config
- vue/ — Composition API + Pinia
- linux/ — System diagnostic cheatsheet
- database/ — PostgreSQL + MySQL patterns
- browser/ — Chromium CLI + E2E testing
- security/ — SAST audit (OWASP Top 10)
- best-practices/ — Clean Code + SOLID + Clean Architecture
- deploy/ — Docker multi-stack + OpenClaw ops
- + INDEX.md como guia de navegacao

.learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
2026-05-19 21:03:25 -03:00

3.6 KiB

Next.js — Best Practices

Extraído de skills: next-best-practices v0.1.0 + nextjs-patterns v1.0.0

🏗️ Estrutura de Projeto (App Router)

app/
├── (marketing)/           # Route group — não aparece na URL
│   └── page.tsx
├── (dashboard)/
│   ├── layout.tsx         # Layout compartilhado do grupo
│   ├── [id]/
│   │   └── page.tsx       # Página dinâmica
│   └── loading.tsx        # Suspense boundary automático
├── api/
│   └── route.ts            # Route Handler (REST/GraphQL)
├── error.tsx               # Error boundary por rota
├── not-found.tsx           # Página 404
└── layout.tsx              # Root layout (obrigatório)

🖥️ Server vs Client Components

// ✅ Server Component (padrão) — roda no servidor, nenhum JS no cliente
async function ProductList() {
  // DB query acontece no servidor!
  const products = await db.product.findMany();
  return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}

// ✅ Client Component — só quando precisar de interatividade
"use client";
import { useState } from "react";
export function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(n => n + 1)}>{count}</button>;
}

// ✅ Server Actions — mutations sem criar API routes
async function createProduct(formData: FormData) {
  "use server";
  await db.product.create({ data: { ... } });
}

📊 Padrões de Data Fetching

Padrão Quando usar Exemplo
Server Component await Read, página ou componente const posts = await db.post.findMany()
Server Actions Write/mutations, formulários "use server" + revalidatePath
Route Handlers REST/GraphQL API, webhooks, integrações app/api/users/route.ts
use() hook Ler promise em Client Components const data = use(fetchData())

Evitando Data Waterfalls

// ❌ Ruim — sequencial, cada await aguarda o anterior
async function Page() {
  const user = await getUser();      // 200ms
  const posts = await getPosts();     // 300ms (só começa após user)
  return <Profile user={user} posts={posts} />;
}

// ✅ Bom — paralelo
async function Page() {
  const [user, posts] = await Promise.all([
    getUser(),     // começa imediatamente
    getPosts(),    // começa imediatamente
  ]);
  return <Profile user={user} posts={posts} />;
}

🗄️ Database Client Singleton

// lib/db.ts — criar uma vez por request (Server Component boundary)
import { PrismaClient } from '@prisma/client';
const globalForDb = globalThis as unknown as { prisma: PrismaClient };

export const db = globalForDb.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForDb.prisma = db;

🔄 Caching — 4 Camadas Next.js

Camada O que cacheia Controlado por
Request Memoization fetch, React.cache() automático por request
Data Cache fetch responses next: { revalidate: 60 }
Full Route Cache Render completo export const revalidate
Router Cache Estado do cliente após navegação next/router
// Revalidar após 60s
fetch(url, { next: { revalidate: 60 } });

// Não cachear (sempre fresh)
fetch(url, { next: { revalidate: 0 } });

⚠️ Regra Fundamental

"use client" é uma barreira de escape. Tudo dentro de um arquivo com essa diretiva roda no navegador. Tudo SEM ela roda no servidor. Quando possível, fique no servidor.