/react-best-practices
React and Next.js performance optimization patterns. Use BEFORE implementing any React code to ensure best practices are followed.
$ npx -y skills add avivk5498/the-claude-protocol --skill react-best-practices --agent claude-codeHow 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.mdname: 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({ statRead more
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({ statEnforcement-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.
Repo: avivk5498/the-claude-protocol
Other skills on the-claude-protocol.
- /create-beads-orchestration
Bootstrap lean multi-agent orchestration with beads task tracking. Use for projects needing agent delegation without heavy MCP overhead.
Open skill - /create-beads-orchestration
Bootstrap lean multi-agent orchestration with beads task tracking. Use for projects needing agent delegation without heavy MCP overhead.
Open skill - /subagents-discipline
Invoke at the start of any implementation task to enforce verification-first development
Open skill - /subagents-discipline
Core engineering principles for implementation tasks
Open skill

