Skip to content
Agent Orchestration
Skill

/react-best-practices

React and Next.js performance optimization patterns. Use BEFORE implementing any React code to ensure best practices are followed.

From plugin
the-claude-protocol
3435 skills7 agents
Install
$ npx -y skills add avivk5498/the-claude-protocol --skill react-best-practices --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/react-best-practices

Context preview

The summary Claude sees to decide when to auto-load this skill.

React and Next.js performance optimization patterns. Use BEFORE implementing any React code to ensure best practices are followed.

SKILL.md

react-best-practices.SKILL.md
name: react-best-practices
description: React and Next.js performance optimization patterns. Use BEFORE implementing any React code to ensure best practices are followed.

React Best Practices

**Version 1.0.0** Source: Vercel Engineering (vercel-labs/agent-skills)

> **Note:** > This document is for agents and LLMs to follow when maintaining, > generating, or refactoring React and Next.js codebases. Contains 40+ rules across 8 categories, prioritized by impact.

---

How to Use This Skill

**Before implementing ANY React/Next.js code:**

1. Review the relevant sections based on what you're building 2. Apply the patterns as you write code 3. Use the "Incorrect" vs "Correct" examples as templates

**Priority order:** Eliminating Waterfalls > Bundle Size > Server-Side > Client-Side > Re-renders > Rendering > JS Perf > Advanced

---

Quick Reference: Critical Rules

Top 5 Rules (Always Apply)

1. **Promise.all() for independent operations** - Never sequential awaits for independent data 2. **Avoid barrel file imports** - Import directly from source files 3. **Dynamic imports for heavy components** - Lazy-load Monaco, charts, etc. 4. **Parallel data fetching with component composition** - Structure RSC for parallelism 5. **Minimize serialization at RSC boundaries** - Only pass needed fields to client

---

1. Eliminating Waterfalls

**Impact: CRITICAL** - Waterfalls are the #1 performance killer.

1.1 Defer Await Until Needed

Move `await` into branches where actually used.

// BAD: blocks both branches
async function handleRequest(userId: string, skipProcessing: boolean) {
  const userData = await fetchUserData(userId)
  if (skipProcessing) return { skipped: true }
  return processUserData(userData)
}

// GOOD: only blocks when needed
async function handleRequest(userId: string, skipProcessing: boolean) {
  if (skipProcessing) return { skipped: true }
  const userData = await fetchUserData(userId)
  return processUserData(userData)
}

1.2 Promise.all() for Independent Operations

// BAD: 3 round trips
const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()

// GOOD: 1 round trip
const [user, posts, comments] = await Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments()
])

1.3 Strategic Suspense Boundaries

// BAD: wrapper blocked by data
async function Page() {
  const data = await fetchData()
  return (
    <div>
      <Sidebar />
      <DataDisplay data={data} />
      <Footer />
    </div>
  )
}

// GOOD: wrapper shows immediately
function Page() {
  return (
    <div>
      <Sidebar />
      <Suspense fallback={<Skeleton />}>
        <DataDisplay />
      </Suspense>
      <Footer />
    </div>
  )
}

---

2. Bundle Size Optimization

**Impact: CRITICAL** - Reduces TTI and LCP.

2.1 Avoid Barrel File Imports

// BAD: loads 1,583 modules
import { Check, X, Menu } from 'lucide-react'

// GOOD: loads only 3 modules
import Check from 'lucide-react/dist/esm/icons/check'
import X from 'lucide-react/dist/esm/icons/x'
import Menu from 'lucide-react/dist/esm/icons/menu'

// ALTERNATIVE: Next.js 13.5+ config
// next.config.js
module.exports = {
  experimental: {
    optimizePackageImports: ['lucide-react', '@mui/material']
  }
}

2.2 Dynamic Imports for Heavy Components

// BAD: Monaco bundles with main chunk (~300KB)
import { MonacoEditor } from './monaco-editor'

// GOOD: Monaco loads on demand
import dynamic from 'next/dynamic'
const MonacoEditor = dynamic(
  () => import('./monaco-editor').then(m => m.MonacoEditor),
  { ssr: false }
)

2.3 Defer Non-Critical Libraries

// BAD: blocks initial bundle
import { Analytics } from '@vercel/analytics/react'

// GOOD: loads after hydration
import dynamic from 'next/dynamic'
const Analytics = dynamic(
  () => import('@vercel/analytics/react').then(m => m.Analytics),
  { ssr: false }
)

2.4 Preload on User Intent

function EditorButton({ onClick }: { onClick: () => void }) {
  const preload = () => {
    if (typeof window !== 'undefined') {
      void import('./monaco-editor')
    }
  }
  return (
    <button onMouseEnter={preload} onFocus={preload} onClick={onClick}>
      Open Editor
    </button>
  )
}

---

3. Server-Side Performance

**Impact: HIGH**

3.1 Minimize Serialization at RSC Boundaries

// BAD: serializes all 50 fields
async function Page() {
  const user = await fetchUser()  // 50 fields
  return <Profile user={user} />
}

// GOOD: serializes only needed fields
async function Page() {
  const user = await fetchUser()
  return <Profile name={user.name} avatar={user.avatar} />
}

3.2 Parallel Data Fetching with Component Composition

// BAD: Sidebar waits for Header's fetch
export default async function Page() {
  const header = await fetchHeader()
  return (
    <div>
      <div>{header}</div>
      <Sidebar />
    </div>
  )
}

// GOOD: both fetch simultaneously
async function Header() {
  const data = await fetchHeader()
  return <div>{data}</div>
}

async function Sidebar() {
  const items = await fetchSidebarItems()
  return <nav>{items.map(renderItem)}</nav>
}

export default function Page() {
  return (
    <div>
      <Header />
      <Sidebar />
    </div>
  )
}

3.3 Per-Request Deduplication with React.cache()

import { cache } from 'react'

export const getCurrentUser = cache(async () => {
  const session = await auth()
  if (!session?.user?.id) return null
  return await db.user.findUnique({ where: { id: session.user.id } })
})

3.4 Use after() for Non-Blocking Operations

import { after } from 'next/server'

export async function POST(request: Request) {
  await updateDatabase(request)

  // Log after response is sent
  after(async () => {
    const userAgent = (await headers()).get('user-agent')
    logUserAction({ userAgent })
  })

  return Response.json({ stat
Read more
Ships withthe-claude-protocol

Enforcement-first orchestration for Claude Code. Every agent tracked. Every decision logged. Nothing gets lost. Claude Code plans great. Without structure, nothing survives past one session. macOS and Linux.

Get the whole plugin