React and Next.js performance optimization guidelines from Vercel Engineering. Use when writing, reviewing, or refactoring React/Next.js code.
57 rules across 8 categories, prioritized by impact.
This skill applies only to React and Next.js projects. Do not apply these rules to non-React codebases. Detect applicability by checking for react or next in package.json dependencies, or the presence of .tsx/.jsx files and next.config.*.
Sequential async operations are the single biggest performance killer. Always parallelize independent work.
await into the branch where the result is actually used; do not await at the top of a function if only one code path needs the valuePromise.all() for independent async operations instead of sequential await callsbetter-all or manual promise chaining for partially dependent async operations where some promises depend on others but not all are sequentialawait them as late as possible to maximize concurrency<Suspense> boundaries to stream content incrementally instead of blocking the entire pageEvery kilobyte shipped to the client costs load time. Minimize what gets bundled.
import { X } from 'lib/X'), never from barrel index.ts files that pull in the entire packagenext/dynamic or React.lazy() for heavy components (charts, editors, maps) that are not needed at initial rendernext/script with strategy="afterInteractive" or "lazyOnload"import()Maximize server efficiency and minimize data sent to the client.
React.cache() to deduplicate identical data fetches within a single server render pass (per-request memoization)lru-cache) for cross-request caching of expensive computations or external API calls on the serverReact.cache()after() API (Next.js 15+) for non-blocking post-response work like logging, analytics, and cache revalidationEfficient client-side data management reduces network overhead and improves responsiveness.
fetch in useEffect{ passive: true } for scroll and touch event listeners to avoid blocking the main thread during scrollingPrevent unnecessary re-renders to keep the UI responsive under load.
React.memo() wrapped components so they skip re-render when their props have not changedReact.memo() shallow comparison works correctlyuseEffect/useMemo/useCallback dependencies instead of objects to avoid false invalidationisLoaded) rather than raw data objects to minimize re-render surfaceuseMemo) instead of using useEffect + setState, which causes an extra render cyclesetState(prev => ...) form so callbacks do not depend on the current state value, making them stable for useCallbackuseState(() => expensiveInit()) instead of calling expensiveInit() directly, so it only runs on mountuseMemo; the overhead of memoization exceeds the cost of recomputing a + buseEffect; effects are for synchronization with external systems, not for responding to user actionsstartTransition for non-urgent state updates (filtering, tab switching) so they do not block urgent updates like typinguseRef for values that change frequently but do not need to trigger a re-render (scroll position, timers, animation frames)Optimize how the DOM is updated and painted.
<div> wrapper around SVG elements rather than the SVG element itself; DOM updates on SVGs are slowercontent-visibility: auto for long scrollable lists to skip rendering of off-screen items<script> in <head> (via Next.js beforeInteractive) to set client-only data (theme, locale) before first paint to prevent hydration flickersuppressHydrationWarning on elements with expected server/client mismatches (timestamps, random IDs) instead of restructuring code<Activity> component (React 19+) for show/hide toggling that preserves component state without unmountingcond ? <A/> : null) instead of && for conditional rendering to avoid accidentally rendering 0 or "" to the DOMuseTransition with isPending for loading states over manual useState booleans; it integrates with Suspense and concurrent featuresMicro-optimizations that compound in hot paths and large datasets.
className or el.style.cssText in a single operation instead of setting individual style propertiesMap or plain object index for repeated lookups on arrays instead of using .find() or .filter() in loopsconst val = obj.a.b.c) in a local variable when accessed multiple times in a loopMap when the function is called repeatedly with the same argumentslocalStorage.getItem() / sessionStorage.getItem() reads in a variable; do not read from storage on every render or function call.filter().map().reduce() chains into a single loop to avoid creating intermediate arraysarray.length before performing expensive comparisons or operations on array elements (fail fast)if blocksRegExp creation outside loops and hot paths; compile the regex once at module level and reuse itfor loop with comparisons for finding min/max values instead of Math.max(...arr) which has call stack limits on large arraysSet for membership checks and Map for key-value lookups instead of arrays; both provide O(1) access vs O(n) for array .includes().toSorted(), .toReversed(), .toSpliced() for immutable array operations instead of cloning then mutatingSpecialized patterns for complex applications.
useRef) when they need to be stable across renders but always call the latest versionReact.cache(), not inside componentsuseLatest(value) hook that returns a ref always pointing to the latest value, providing a stable reference for callbacks without re-rendernpx skills add citruseason/vercel-react-best-practices下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer