# /nextjs - Performance Rules (Vercel Engineering)

57 regras priorizadas por impacto. Fonte: Vercel Engineering react-best-practices.

## 1. Eliminating Waterfalls (CRITICAL)

```tsx
// ❌ ERRADO: Awaits sequenciais (waterfall)
const session = await getSession()
const config = await getConfig()
const user = await getUser(session.userId)

// ✅ CORRECTO: Paralelo com Promise.all()
const [session, config] = await Promise.all([
  getSession(),
  getConfig()
])
const user = await getUser(session.userId) // depende de session

// ✅ CORRECTO: Suspense boundaries estratégicos
async function Page() {
  return (
    <Layout>
      <Suspense fallback={<HeaderSkeleton />}>
        <Header />
      </Suspense>
      <Suspense fallback={<ContentSkeleton />}>
        <SlowContent />
      </Suspense>
    </Layout>
  )
}

// ✅ CORRECTO: Defer await até necessário
async function handler(req) {
  const dataPromise = fetchData() // inicia imediatamente
  if (req.method === 'HEAD') return new Response() // early return
  const data = await dataPromise // só espera quando precisa
  return Response.json(data)
}
```

**Regras:**
- `async-defer-await`: Mover await para onde é necessário
- `async-dependency-parallel`: Usar `better-all` para dependências parciais
- `async-prevent-waterfall`: Iniciar promises antes de awaitar
- `async-promise-all`: `Promise.all()` para operações independentes
- `async-suspense-boundaries`: Suspense para UI progressiva

---

## 2. Bundle Size (CRITICAL)

```tsx
// ❌ ERRADO: Barrel file imports
import { Button, Icon } from '@mui/material' // carrega tudo

// ✅ CORRECTO: Import directo
import Button from '@mui/material/Button'

// ✅ CORRECTO: optimizePackageImports (Next.js 13.5+)
module.exports = {
  experimental: {
    optimizePackageImports: ['lucide-react', '@mui/material', 'lodash']
  }
}

// ✅ CORRECTO: Dynamic imports para componentes pesados
const MonacoEditor = dynamic(() => import('./MonacoEditor'), {
  ssr: false,
  loading: () => <EditorSkeleton />
})

// ✅ CORRECTO: Preload em hover/focus
function FeatureButton() {
  return (
    <button
      onMouseEnter={() => import('./HeavyFeature')}
      onClick={openFeature}
    >
      Open Feature
    </button>
  )
}
```

---

## 3. Server-Side Performance (HIGH)

```tsx
// ✅ Server Actions: autenticar SEMPRE
'use server'
async function updateProfile(formData: FormData) {
  const session = await auth() // 1. Autenticar
  if (!session) throw new Error('Unauthorized')
  const data = schema.parse(formData) // 2. Validar
  await db.user.update({ where: { id: session.userId }, data })
  revalidatePath('/profile')
}

// ✅ React.cache() para deduplicação per-request
const getUser = cache(async (id: string) => {
  return db.user.findUnique({ where: { id } })
})

// ✅ after() para operações não-bloqueantes
import { after } from 'next/server'
async function submitForm(data: FormData) {
  const result = await processForm(data)
  after(async () => {
    await logAnalytics(result) // executa APÓS resposta enviada
  })
  return result
}
```

---

## 4. Client-Side Data Fetching (MEDIUM-HIGH)

```tsx
// ✅ SWR para deduplicação automática
const { data, isLoading } = useSWR('/api/user', fetcher)

// ✅ Passive event listeners para scroll performance
element.addEventListener('scroll', handler, { passive: true })

// ✅ localStorage versionado com try-catch
const key = 'userConfig:v2'
try {
  const data = JSON.parse(localStorage.getItem(key) ?? '{}')
} catch { /* incognito mode, quota exceeded */ }
```

---

## 5. Re-render Optimization (MEDIUM)

```tsx
// ✅ Derived state: calcular durante render, NÃO em useEffect
function FilteredList({ items, query }: Props) {
  const filtered = items.filter(i => i.name.includes(query)) // derive!
  return <List items={filtered} />
}

// ✅ Functional setState para evitar stale closures
setItems(curr => [...curr, ...newItems]) // ✅
// setItems([...items, ...newItems]) // ❌ stale closure

// ✅ Lazy state initialization
const [index] = useState(() => buildSearchIndex(items)) // computa 1x

// ✅ useTransition para updates não-urgentes
const [isPending, startTransition] = useTransition()
startTransition(() => setSearchResults(results))

// ✅ Extrair defaults não-primitivos para module scope
const EMPTY_ARRAY: string[] = []
const NOOP = () => {}
function Component({ items = EMPTY_ARRAY, onClick = NOOP }) { ... }

// ✅ Dependências de effect: primitivos, não objectos
useEffect(() => { fetchUser(userId) }, [userId]) // ✅ primitivo
```

---

## 6. Rendering Performance (MEDIUM)

```tsx
// ✅ Hoist static JSX fora do componente
const skeleton = <div className="skeleton h-4 w-full" /> // module scope

// ✅ Conditional rendering explícito (evitar && com falsy)
{count > 0 ? <Badge>{count}</Badge> : null} // ✅
// {count && <Badge>{count}</Badge>} // ❌ renderiza "0"

// ✅ useTransition em vez de useState manual para loading
const [isPending, startTransition] = useTransition()
```

---

## 7. JavaScript Performance (LOW-MEDIUM)

```tsx
// ✅ Index Maps para lookups repetidos O(1) vs O(n)
const userMap = new Map(users.map(u => [u.id, u]))
orders.map(o => ({ ...o, user: userMap.get(o.userId) }))

// ✅ Set para membership checks
const allowedIds = new Set(['a', 'b', 'c'])
if (allowedIds.has(id)) { ... } // O(1)

// ✅ toSorted() para imutabilidade (React-safe)
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name))
// items.sort() // ❌ muta o array original

// ✅ Combinar iterações de array
const { active, expired } = items.reduce((acc, item) => {
  if (item.active) acc.active.push(item)
  else acc.expired.push(item)
  return acc
}, { active: [], expired: [] })
```

---

## 8. Advanced Patterns (LOW)

```tsx
// ✅ Init uma vez, não por mount (Strict Mode remonta)
let didInit = false
function App() {
  useEffect(() => {
    if (didInit) return
    didInit = true
    initializeAnalytics()
  }, [])
}

// ✅ useEffectEvent para callbacks estáveis em effects
const onTick = useEffectEvent((value) => {
  console.log(value) // sempre valor mais recente
})
useEffect(() => {
  const id = setInterval(() => onTick(count), 1000)
  return () => clearInterval(id)
}, []) // sem dependência de count!
```

---

## Resumo de Impacto

| Prioridade | Categoria | Ganho |
|---|---|---|
| CRITICAL | Waterfalls | 2-10x latência |
| CRITICAL | Bundle Size | 200-800ms load |
| HIGH | Server-Side | RSC payload, TTI |
| MEDIUM-HIGH | Client Fetch | Request dedup |
| MEDIUM | Re-renders | UI responsiveness |
| MEDIUM | Rendering | Paint performance |
| LOW-MEDIUM | JS Perf | CPU cycles |
