Skip to main content
The React Best Practices skill provides comprehensive performance optimization guidelines from Vercel Engineering, containing 57 rules across 8 categories prioritized by impact.

Overview

This skill guides automated refactoring and code generation with proven patterns for:
  • Eliminating request waterfalls
  • Optimizing bundle size
  • Improving server-side performance
  • Reducing client-side overhead
  • Minimizing unnecessary re-renders
Rules are prioritized from CRITICAL (high user impact) to LOW (incremental gains) to guide optimization efforts.

Rule Categories

1. Eliminating Waterfalls (CRITICAL)

Waterfalls are the #1 performance killer. Each sequential await adds full network latency.
async function handleRequest(userId: string) {
  const userData = await fetchUserData(userId)
  const config = await fetchConfig()
  const profile = await fetchProfile(userData.id)
  return { userData, config, profile }
}
// 3 sequential requests = 3x latency
Key Rules:
  • async-defer-await - Move await into branches where actually used
  • async-parallel - Use Promise.all() for independent operations
  • async-dependencies - Use better-all for partial dependencies
  • async-suspense-boundaries - Use Suspense to stream content

2. Bundle Size Optimization (CRITICAL)

Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
import { Check, X, Menu } from 'lucide-react'
// Loads 1,583 modules, takes ~2.8s extra in dev
// Runtime cost: 200-800ms on every cold start
Key Rules:
  • bundle-barrel-imports - Import directly, avoid barrel files
  • bundle-dynamic-imports - Use next/dynamic for heavy components
  • bundle-defer-third-party - Load analytics/logging after hydration
  • bundle-conditional - Load modules only when feature is activated

3. Server-Side Performance (HIGH)

Optimizing server-side rendering eliminates server-side waterfalls and reduces response times.
'use server'

export async function deleteUser(userId: string) {
  // Anyone can call this! No auth check
  await db.user.delete({ where: { id: userId } })
  return { success: true }
}
Key Rules:
  • server-auth-actions - Authenticate server actions like API routes
  • server-cache-react - Use React.cache() for per-request deduplication
  • server-cache-lru - Use LRU cache for cross-request caching
  • server-parallel-fetching - Restructure components to parallelize fetches

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

function UserList() {
  const [users, setUsers] = useState([])
  useEffect(() => {
    fetch('/api/users')
      .then(r => r.json())
      .then(setUsers)
  }, [])
}
// Each instance fetches independently
Key Rules:
  • client-swr-dedup - Use SWR for automatic request deduplication
  • client-event-listeners - Deduplicate global event listeners
  • client-passive-event-listeners - Use passive listeners for scroll

5. Re-render Optimization (MEDIUM)

function Form() {
  const [firstName, setFirstName] = useState('First')
  const [lastName, setLastName] = useState('Last')
  const [fullName, setFullName] = useState('')

  useEffect(() => {
    setFullName(firstName + ' ' + lastName)
  }, [firstName, lastName])

  return <p>{fullName}</p>
}
Key Rules:
  • rerender-defer-reads - Don’t subscribe to state only used in callbacks
  • rerender-memo - Extract expensive work into memoized components
  • rerender-functional-setstate - Use functional setState for stable callbacks
  • rerender-derived-state-no-effect - Derive state during render, not effects

6. Rendering Performance (MEDIUM)

function LoadingSpinner() {
  return (
    <svg className="animate-spin" width="24" height="24">
      <circle cx="12" cy="12" r="10" />
    </svg>
  )
}
// No hardware acceleration
Key Rules:
  • rendering-animate-svg-wrapper - Animate div wrapper, not SVG element
  • rendering-content-visibility - Use content-visibility for long lists
  • rendering-hoist-jsx - Extract static JSX outside components

7. JavaScript Performance (LOW-MEDIUM)

function processOrders(orders: Order[], users: User[]) {
  return orders.map(order => ({
    ...order,
    user: users.find(u => u.id === order.userId)
  }))
}
// For 1000 orders × 1000 users: 1M operations
Key Rules:
  • js-index-maps - Build Map for repeated lookups
  • js-set-map-lookups - Use Set/Map for O(1) lookups
  • js-tosorted-immutable - Use toSorted() for immutability

8. Advanced Patterns (LOW)

function Comp() {
  useEffect(() => {
    loadFromStorage()
    checkAuthToken()
  }, [])
}
// Runs twice in dev, re-runs on remount
Key Rules:
  • advanced-init-once - Initialize app once per app load
  • advanced-event-handler-refs - Store event handlers in refs
  • advanced-use-latest - useLatest for stable callback refs

When to Apply

Load this skill when:
  • Writing new React components or Next.js pages
  • Implementing data fetching (client or server-side)
  • Reviewing code for performance issues
  • Refactoring existing React/Next.js code
  • Optimizing bundle size or load times

Impact Metrics

Rules are prioritized by measured impact:
CategoryExample Improvement
Eliminating Waterfalls2-10× faster page loads
Bundle Optimization15-70% faster dev boot, 28% faster builds
Server Performance40% faster cold starts
Re-render Optimization50-90% fewer renders

Skill Structure

.github/skills/react-best-practices/
├── SKILL.md           # Quick reference (this page)
├── AGENTS.md          # Full documentation with all 57 rules
└── rules/             # Individual rule files
    ├── async-parallel.md
    ├── bundle-barrel-imports.md
    ├── server-auth-actions.md
    └── ...

References

Full documentation: .github/skills/react-best-practices/AGENTS.md External resources:
Start with CRITICAL and HIGH priority rules for maximum impact, then apply MEDIUM and LOW rules during optimization passes.

Build docs developers (and LLMs) love