AgentSkillsCN

vercel-react-best-practices

在编写 React/Next.js 代码、排查性能问题、优化包体积、实现数据获取逻辑,或对现有 React 组件进行重构时,可选用此功能。

SKILL.md
--- frontmatter
name: vercel-react-best-practices
description: Use when writing React/Next.js code, reviewing for performance issues, optimizing bundle size, implementing data fetching, or refactoring existing React components

Vercel React Best Practices

Overview

Comprehensive performance optimization guide for React and Next.js applications. Contains 57 rules across 8 categories, prioritized by impact to guide code generation and refactoring.

Core principle: Eliminate waterfalls, minimize bundle size, and optimize rendering - in that priority order.

When to Use

  • 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

Quick Reference by Priority

1. Eliminating Waterfalls (CRITICAL)

RuleDescription
async-defer-awaitMove await into branches where actually used
async-parallelUse Promise.all() for independent operations
async-dependenciesUse better-all for partial dependencies
async-api-routesStart promises early, await late in API routes
async-suspense-boundariesUse Suspense to stream content

Pattern - Parallel fetching:

typescript
// ❌ Sequential (waterfall)
const user = await getUser(id);
const posts = await getPosts(id);

// ✅ Parallel
const [user, posts] = await Promise.all([
  getUser(id),
  getPosts(id)
]);

2. Bundle Size Optimization (CRITICAL)

RuleDescription
bundle-barrel-importsImport directly, avoid barrel files
bundle-dynamic-importsUse next/dynamic for heavy components
bundle-defer-third-partyLoad analytics/logging after hydration
bundle-conditionalLoad modules only when feature is activated
bundle-preloadPreload on hover/focus for perceived speed

Pattern - Direct imports:

typescript
// ❌ Barrel import (pulls entire module)
import { Button } from '@/components';

// ✅ Direct import
import { Button } from '@/components/Button';

Pattern - Dynamic import:

typescript
// ❌ Static import of heavy component
import HeavyChart from './HeavyChart';

// ✅ Dynamic import
const HeavyChart = dynamic(() => import('./HeavyChart'), {
  loading: () => <ChartSkeleton />
});

3. Server-Side Performance (HIGH)

RuleDescription
server-auth-actionsAuthenticate server actions like API routes
server-cache-reactUse React.cache() for per-request deduplication
server-cache-lruUse LRU cache for cross-request caching
server-dedup-propsAvoid duplicate serialization in RSC props
server-serializationMinimize data passed to client components
server-parallel-fetchingRestructure components to parallelize fetches
server-after-nonblockingUse after() for non-blocking operations

Pattern - React.cache for deduplication:

typescript
// ✅ Cached function - called multiple times, executes once per request
const getUser = cache(async (id: string) => {
  return await db.user.findUnique({ where: { id } });
});

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

RuleDescription
client-swr-dedupUse SWR for automatic request deduplication
client-event-listenersDeduplicate global event listeners
client-passive-event-listenersUse passive listeners for scroll
client-localstorage-schemaVersion and minimize localStorage data

5. Re-render Optimization (MEDIUM)

RuleDescription
rerender-defer-readsDon't subscribe to state only used in callbacks
rerender-memoExtract expensive work into memoized components
rerender-memo-with-default-valueHoist default non-primitive props
rerender-dependenciesUse primitive dependencies in effects
rerender-derived-stateSubscribe to derived booleans, not raw values
rerender-derived-state-no-effectDerive state during render, not effects
rerender-functional-setstateUse functional setState for stable callbacks
rerender-lazy-state-initPass function to useState for expensive values
rerender-simple-expression-in-memoAvoid memo for simple primitives
rerender-move-effect-to-eventPut interaction logic in event handlers
rerender-transitionsUse startTransition for non-urgent updates
rerender-use-ref-transient-valuesUse refs for transient frequent values

Pattern - Functional setState:

typescript
// ❌ Creates new callback reference each render
const increment = () => setCount(count + 1);

// ✅ Stable callback, no dependency on count
const increment = useCallback(() => setCount(c => c + 1), []);

Pattern - Lazy state initialization:

typescript
// ❌ Expensive computation runs every render
const [data] = useState(expensiveComputation());

// ✅ Only runs once
const [data] = useState(() => expensiveComputation());

6. Rendering Performance (MEDIUM)

RuleDescription
rendering-animate-svg-wrapperAnimate div wrapper, not SVG element
rendering-content-visibilityUse content-visibility for long lists
rendering-hoist-jsxExtract static JSX outside components
rendering-svg-precisionReduce SVG coordinate precision
rendering-hydration-no-flickerUse inline script for client-only data
rendering-hydration-suppress-warningSuppress expected mismatches
rendering-activityUse Activity component for show/hide
rendering-conditional-renderUse ternary, not && for conditionals
rendering-usetransition-loadingPrefer useTransition for loading state

Pattern - Conditional rendering:

typescript
// ❌ Can render 0 or false
{count && <Component />}

// ✅ Always renders nothing or component
{count > 0 ? <Component /> : null}

7. JavaScript Performance (LOW-MEDIUM)

RuleDescription
js-batch-dom-cssGroup CSS changes via classes or cssText
js-index-mapsBuild Map for repeated lookups
js-cache-property-accessCache object properties in loops
js-cache-function-resultsCache function results in module-level Map
js-cache-storageCache localStorage/sessionStorage reads
js-combine-iterationsCombine multiple filter/map into one loop
js-length-check-firstCheck array length before expensive comparison
js-early-exitReturn early from functions
js-hoist-regexpHoist RegExp creation outside loops
js-min-max-loopUse loop for min/max instead of sort
js-set-map-lookupsUse Set/Map for O(1) lookups
js-tosorted-immutableUse toSorted() for immutability

Pattern - Set for lookups:

typescript
// ❌ O(n) lookup
const isSelected = selectedIds.includes(id);

// ✅ O(1) lookup
const selectedSet = new Set(selectedIds);
const isSelected = selectedSet.has(id);

8. Advanced Patterns (LOW)

RuleDescription
advanced-event-handler-refsStore event handlers in refs
advanced-init-onceInitialize app once per app load
advanced-use-latestuseLatest for stable callback refs

Red Flags

Watch for these anti-patterns during code review:

  • Sequential awaits for independent data fetches
  • Barrel file imports (import { x } from '@/components')
  • Large static imports for conditionally-used components
  • State subscriptions for values only used in callbacks
  • Array.includes() in loops (use Set instead)
  • useEffect for derived state (compute during render)
  • Non-primitive effect dependencies causing infinite loops

Supporting Files

  • AGENTS.md - Full compiled document with all 40+ rules expanded with detailed explanations and code examples
  • rules/*.md - Individual rule files for detailed reference (59 files)

References