name: vercel-react-best-practices description: React and Next.js performance optimization guidelines from Vercel Engineering. Use when writing, reviewing, or refactoring React components or Next.js App Router code for performance (waterfalls, bundle size, server/client performance, rerenders, rendering performance). metadata: owner: "skills-steward" last_updated: 2026-02-17 status: "active" upstream: url: "https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices" repo: "vercel-labs/agent-skills" path: "skills/react-best-practices/SKILL.md" ref: "e23951b8cad2f4b1e7e176c5731127c1263fe86f" license: "MIT"
Vercel React Best Practices
Performance optimization guide for React and Next.js applications (adapted from Vercel Engineering's agent skill; see references/upstream.md for attribution).
When to Apply
Use this skill when:
- Writing new React components or Next.js pages/layouts
- Implementing data fetching (server-side or client-side)
- Reviewing code for performance issues (slow TTFB, slow LCP, janky interactions)
- Refactoring React/Next.js code where perf regressions are likely
- Optimizing bundle size or load times
Do not use this skill when:
- The task is purely visual/styling with no behavioral change (use
react-component-devinstead) - The task is primarily caching/invalidation strategy (use
cache-componentsinstead)
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Eliminating Waterfalls | CRITICAL | async- |
| 2 | Bundle Size Optimization | CRITICAL | bundle- |
| 3 | Server-Side Performance | HIGH | server- |
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | client- |
| 5 | Re-render Optimization | MEDIUM | rerender- |
| 6 | Rendering Performance | MEDIUM | rendering- |
| 7 | JavaScript Performance | LOW-MEDIUM | js- |
| 8 | Advanced Patterns | LOW | advanced- |
Quick Reference (Rule IDs)
These are the rule IDs used by the upstream skill; use them as a vocabulary when discussing fixes.
1) Eliminating Waterfalls (CRITICAL)
async-defer-await- Moveawaitinto branches where actually usedasync-parallel- UsePromise.all()for independent operationsasync-dependencies- Use partial dependency parallelism (upstream calls this "better-all")async-api-routes- Start promises early, await late in API routesasync-suspense-boundaries- UseSuspenseto stream content
2) Bundle Size Optimization (CRITICAL)
bundle-barrel-imports- Import directly, avoid barrel filesbundle-dynamic-imports- Usenext/dynamicfor heavy componentsbundle-defer-third-party- Load analytics/logging after hydrationbundle-conditional- Load modules only when the feature is activatedbundle-preload- Preload on hover/focus for perceived speed
3) Server-Side Performance (HIGH)
server-auth-actions- Authenticate server actions like API routesserver-cache-react- UseReact.cache()for per-request deduplicationserver-cache-lru- Use an LRU cache for cross-request cachingserver-dedup-props- Avoid duplicate serialization in RSC propsserver-serialization- Minimize data passed to client componentsserver-parallel-fetching- Restructure components to parallelize fetchesserver-after-nonblocking- Useafter()for non-blocking operations
4) Client-Side Data Fetching (MEDIUM-HIGH)
client-swr-dedup- Use SWR for automatic request deduplicationclient-event-listeners- Deduplicate global event listenersclient-passive-event-listeners- Use passive listeners for scrollclient-localstorage-schema- Version and minimize localStorage data
5) Re-render Optimization (MEDIUM)
rerender-defer-reads- Don't subscribe to state only used in callbacksrerender-memo- Extract expensive work into memoized componentsrerender-memo-with-default-value- Hoist default non-primitive propsrerender-dependencies- Use primitive dependencies in effectsrerender-derived-state- Subscribe to derived booleans, not raw valuesrerender-derived-state-no-effect- Derive state during render, not effectsrerender-functional-setstate- Use functionalsetStatefor stable callbacksrerender-lazy-state-init- Pass function touseStatefor expensive valuesrerender-simple-expression-in-memo- Avoid memo for simple primitivesrerender-move-effect-to-event- Put interaction logic in event handlersrerender-transitions- UsestartTransitionfor non-urgent updatesrerender-use-ref-transient-values- Use refs for transient frequent values
6) Rendering Performance (MEDIUM)
rendering-animate-svg-wrapper- Animate a div wrapper, not the SVG elementrendering-content-visibility- Usecontent-visibilityfor long listsrendering-hoist-jsx- Extract static JSX outside componentsrendering-svg-precision- Reduce SVG coordinate precisionrendering-hydration-no-flicker- Use inline script for client-only datarendering-hydration-suppress-warning- Suppress expected mismatchesrendering-activity- Use Activity component for show/hiderendering-conditional-render- Prefer ternary over&&for conditionalsrendering-usetransition-loading- PreferuseTransitionfor loading state
7) JavaScript Performance (LOW-MEDIUM)
js-batch-dom-css- Group CSS changes via classes orcssTextjs-index-maps- BuildMapfor repeated lookupsjs-cache-property-access- Cache object properties in loopsjs-cache-function-results- Cache function results in module-levelMapjs-cache-storage- Cache localStorage/sessionStorage readsjs-combine-iterations- Combine multiple filter/map into one loopjs-length-check-first- Check array length before expensive comparisonjs-early-exit- Return early from functionsjs-hoist-regexp- HoistRegExpcreation outside loopsjs-min-max-loop- Use a loop for min/max instead of sortjs-set-map-lookups- UseSet/Mapfor O(1) lookupsjs-tosorted-immutable- UsetoSorted()for immutability
8) Advanced Patterns (LOW)
advanced-event-handler-refs- Store event handlers in refsadvanced-init-once- Initialize app once per app loadadvanced-use-latest-useLatestfor stable callback refs
Workflow
- Pick a measurable target (load time, TTFB, interaction jank, bundle size, server cost).
- Start with waterfalls (
async-*): restructure to parallelize independent work and stream viaSuspensewhere appropriate. - Reduce bundle cost (
bundle-*): avoid barrel imports; split heavy, rarely-used UI withnext/dynamic; defer third-party scripts. - Fix server-to-client boundaries (
server-*): avoid over-serializing props; keep heavy compute and secrets on the server; parallelize server fetches. - Fix client fetching (
client-*): dedupe requests, avoid repeated global listeners, keep storage reads/versioning sane. - Remove rerender hotspots (
rerender-*): prefer derived state, stable callbacks, and moving effects to events when possible. - Triage rendering + JS micro-opts (
rendering-*,js-*): only after structural issues are addressed. - Verify with repo gates and any perf checks the task requires:
bun run format:check && bun run lint && bun run typecheck && bun run build && bun run test:unit- Optional:
bun run test:perf(Playwright Web Vitals checks) when relevant
Checklist
- No obvious async waterfalls (parallelize independent work, stream with
Suspense) - No accidental bundle bloat (avoid barrel imports, consider
next/dynamicfor heavy UI) - Server/client boundary is intentional (minimal props; no heavy objects serialized)
- Client data fetching is deduped; no repeated global listeners
- Rerenders are controlled (derived state, stable handlers, memo used only when warranted)
- Rendering issues addressed (long lists, hydration mismatches handled intentionally)
- Repo CI gates pass locally
Additional Resources
- Upstream source + attribution:
references/upstream.md