
React Best Practices
- 1 installs
- 40 repo stars
- Updated August 4, 2026
- akillness/oh-my-skills
react-best-practices is a Vercel-authored Claude Code skill that runs measurement-led React and Next.js performance audits for waterfalls, bundle size, RSC boundaries, hydration, and rerender churn.
About
react-best-practices is a Vercel-authored skill for measurement-led React and Next.js performance audits. A developer uses it to diagnose or refactor a slow React UI, App Router route, or heavy client component by first classifying the dominant bottleneck (waterfalls, bundle size, server/client boundary, rerender churn, hydration cost) and then applying prioritized fixes. It matters because it routes work to the right rule packet instead of guessing at optimizations.
- Measurement-led React and Next.js performance audits before refactoring
- Priority-ranked rule categories: waterfalls, bundle size, RSC boundaries, rerenders
- Classifies the dominant bottleneck into one packet, then applies the smallest fix set
React Best Practices by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,914 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
react-best-practices capabilities & compatibility
- Capabilities
- frontend audit · bundle analysis · rerender optimization · waterfall elimination
- Works with
- vercel
- Use cases
- frontend · code review · refactoring
- IDEs
- vscode · cursor ide
- Pricing
- Free
What react-best-practices says it does
Measurement-led React and Next.js performance skill for routing slow-page work into the right packet before refactoring.
Classify the primary bottleneck before suggesting fixes
npx skills add https://github.com/akillness/oh-my-skills --skill react-best-practicesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 40 |
| Last updated | August 4, 2026 |
| Repository | akillness/oh-my-skills ↗ |
What it does
Diagnose and fix a slow React or Next.js page by classifying the dominant performance bottleneck, then applying prioritized fixes.
Who is it for?
Diagnosing a slow React UI, heavy client component, or Next.js route with excess JavaScript
Skip if: Non-React or non-Next.js performance problems and generic backend tuning
When should I use this skill?
Diagnosing or refactoring a slow React UI, App Router route, or heavy client component
What you get
The dominant bottleneck is classified and fixed with the smallest matching rule packet
- Performance triage classification
- Prioritized fix list
By the numbers
- 8 priority rule categories
- ~40 named optimization rules
Files
Vercel React Best Practices
Measurement-led React and Next.js performance skill for routing slow-page work into the right packet before refactoring. The canonical focus is waterfalls, bundle weight, RSC or client-boundary mistakes, hydration/script cost, and rerender churn; the deep AGENTS.md remains available, but day-to-day use should start from the lighter reference bundle and route-outs.
When to use this skill
Reference these guidelines when:
- 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
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
1. Eliminating Waterfalls (CRITICAL)
async-defer-await- Move await into branches where actually usedasync-parallel- Use Promise.all() for independent operationsasync-dependencies- Use better-all for partial dependenciesasync-api-routes- Start promises early, await late in API routesasync-suspense-boundaries- Use Suspense to stream content
2. Bundle Size Optimization (CRITICAL)
bundle-barrel-imports- Import directly, avoid barrel filesbundle-dynamic-imports- Use next/dynamic for heavy componentsbundle-defer-third-party- Load analytics/logging after hydrationbundle-conditional- Load modules only when feature is activatedbundle-preload- Preload on hover/focus for perceived speed
3. Server-Side Performance (HIGH)
server-cache-react- Use React.cache() for per-request deduplicationserver-cache-lru- Use LRU cache for cross-request cachingserver-serialization- Minimize data passed to client componentsserver-parallel-fetching- Restructure components to parallelize fetchesserver-after-nonblocking- Use after() 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 listeners
5. Re-render Optimization (MEDIUM)
rerender-defer-reads- Don't subscribe to state only used in callbacksrerender-memo- Extract expensive work into memoized componentsrerender-dependencies- Use primitive dependencies in effectsrerender-derived-state- Subscribe to derived booleans, not raw valuesrerender-functional-setstate- Use functional setState for stable callbacksrerender-lazy-state-init- Pass function to useState for expensive valuesrerender-transitions- Use startTransition for non-urgent updates
6. Rendering Performance (MEDIUM)
rendering-animate-svg-wrapper- Animate div wrapper, not SVG elementrendering-content-visibility- Use content-visibility for 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-activity- Use Activity component for show/hiderendering-conditional-render- Use ternary, not && for conditionals
7. JavaScript Performance (LOW-MEDIUM)
js-batch-dom-css- Group CSS changes via classes or cssTextjs-index-maps- Build Map for repeated lookupsjs-cache-property-access- Cache object properties in loopsjs-cache-function-results- Cache function results in module-level Mapjs-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- Hoist RegExp creation outside loopsjs-min-max-loop- Use loop for min/max instead of sortjs-set-map-lookups- Use Set/Map for O(1) lookupsjs-tosorted-immutable- Use toSorted() for immutability
8. Advanced Patterns (LOW)
advanced-event-handler-refs- Store event handlers in refsadvanced-use-latest- useLatest for stable callback refs
Instructions
Step 1: Classify the primary bottleneck before suggesting fixes
Start with references/perf-triage-modes.md and put the task into one dominant packet:
- waterfalls / async sequencing
- bundle weight / lazy loading
- RSC / server-client boundary mistakes
- rerender churn / memoization / context spread
- hydration or third-party script cost
- measurement-first unknowns
Step 2: Use the smallest support packet that answers the question
references/perf-triage-modes.md— choose the main React / Next.js perf mode and first fixesreferences/measurement-and-tooling-checklist.md— decide what to measure with React Profiler, bundle analyzers, Web Vitals, or PR budget toolingreferences/boundaries-and-route-outs.md— keep the boundary withstate-management,web-accessibility,responsive-design, andperformance-optimizationexplicitAGENTS.md— deep rule catalog when a code generation or large refactor pass needs the full 45-rule reference
Step 3: Prefer measurement-led fixes over blanket cargo culting
Use the measurement checklist before recommending broad memoization or cache-heavy rewrites. React Compiler, bundle analyzers, and web vitals instrumentation have changed which old React performance heuristics are still worth applying blindly.
Step 4: Prioritize high-impact fixes first
1. Remove waterfalls and late-started async work. 2. Cut route JS and heavy client boundaries. 3. Fix hydration or script-placement mistakes. 4. Address rerender churn once the larger loading and boundary costs are understood.
Step 5: Route adjacent work out instead of absorbing everything
- App-wide state model choice →
../state-management/SKILL.md - Accessibility-specific failures →
../web-accessibility/SKILL.md - Layout adaptation / overflow / reflow work →
../responsive-design/SKILL.md - Non-React or cross-stack bottlenecks →
../performance-optimization/SKILL.md
Examples
Promise.all for Independent Operations (CRITICAL)
// ❌ Sequential: 3 round trips
const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()
// ✅ Parallel: 1 round trip
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
])Avoid Barrel File Imports (CRITICAL)
// ❌ Imports entire library (200-800ms import cost)
import { Check, X, Menu } from 'lucide-react'
// ✅ Imports only what you need
import Check from 'lucide-react/dist/esm/icons/check'
import X from 'lucide-react/dist/esm/icons/x'Dynamic Imports for Heavy Components (CRITICAL)
// ❌ Monaco bundles with main chunk ~300KB
import { MonacoEditor } from './monaco-editor'
// ✅ Monaco loads on demand
import dynamic from 'next/dynamic'
const MonacoEditor = dynamic(
() => import('./monaco-editor').then(m => m.MonacoEditor),
{ ssr: false }
)Use Functional setState (MEDIUM)
// ❌ Requires state as dependency, stale closure risk
const addItems = useCallback((newItems) => {
setItems([...items, ...newItems])
}, [items])
// ✅ Stable callback, no stale closures
const addItems = useCallback((newItems) => {
setItems(curr => [...curr, ...newItems])
}, [])Best practices
1. Start with the lightest support packet that matches the problem; use AGENTS.md only when the smaller references are not enough. 2. Prefer evidence-led fixes: React Profiler, bundle analyzers, and Web Vitals should shape the recommendation before you prescribe memoization or caching. 3. Treat React Compiler as a live constraint on old memoization folklore; do not default to “wrap everything in memo”. 4. Keep React-specific advice separate from broader frontend concerns: use state-management for app-wide state choices, web-accessibility for WCAG remediation, and performance-optimization for non-React-wide bottlenecks. 5. Escalate to the compatibility alias vercel-react-best-practices only when an existing workflow explicitly requires that exact name. 6. Keep PR-budget and bundle-diff workflows in mind for real team usage; the skill should help with operational audits, not just code-level micro-fixes.
Constraints
Required Rules (MUST)
1. Eliminate waterfalls: Use Promise.all, Suspense 2. Bundle optimization: Prohibit barrel imports, use dynamic imports 3. RSC boundaries: Serialize only the data you need
Prohibited (MUST NOT)
1. Sequential await: Do not run independent fetches sequentially 2. Array mutations: Use toSorted() instead of sort() 3. Inline objects in React.cache: Causes cache misses
References
references/perf-triage-modes.mdreferences/measurement-and-tooling-checklist.mdreferences/boundaries-and-route-outs.mdAGENTS.md- React Documentation
- Next.js Documentation
- SWR
- better-all
- Vercel Blog: Optimizing Package Imports
- Vercel Blog: Dashboard Performance
Metadata
Version
- Current version: 1.1.0
- Last updated: 2026-04-16
- Supported platforms: Claude, ChatGPT, Gemini
- Source: vercel/agent-skills
Related Skills
- performance-optimization: General performance optimization
- state-management: State management
Tags
#React #Next.js #performance #optimization #vercel #waterfalls #bundle-size #RSC #frontend
{
"skill_name": "react-best-practices",
"evals": [
{
"id": 1,
"prompt": "Our Next.js app got slower after we moved more logic into server components. We see sequential fetches, some hydration mismatch, and a heavy dashboard bundle. Audit it.",
"expected_output": "Routes the task to the canonical React performance skill and prioritizes waterfalls, bundle size, hydration mismatch, and RSC boundary fixes.",
"assertions": [
"Output identifies Next.js or React performance work as the core task",
"Output mentions at least two of: waterfalls, bundle size, hydration mismatch, RSC boundary",
"Output does not frame the request as generic state-management or accessibility work",
"Output recommends high-impact React/Next.js performance fixes first"
]
},
{
"id": 2,
"prompt": "Review this React PR for unnecessary rerenders, slow client components, and data-fetching patterns that are hurting the page.",
"expected_output": "Uses the React performance lens rather than generic code review language and includes rerender/data-fetching guidance.",
"assertions": [
"Output treats rerender churn or fetch patterns as primary concerns",
"Output includes React- or Next-specific optimization guidance",
"Output keeps the answer focused on performance rather than broad UI design advice"
]
},
{
"id": 3,
"prompt": "We have a slow ecommerce category page in Next.js. Images are fine, but the page waits on three independent API calls and ships too much JS. What skill should handle this?",
"expected_output": "Clearly selects the canonical React/Next performance skill and classifies the issue as waterfalls plus bundle bloat.",
"assertions": [
"Output selects `react-best-practices` as the right skill",
"Output classifies the problem as sequential fetches or waterfalls",
"Output classifies the problem as bundle size or excess JavaScript",
"Output does not prefer the compatibility alias unless the exact legacy name is required"
]
}
]
}
Boundaries and route-outs
Use this note to keep react-best-practices sharp instead of letting it absorb all frontend or performance work.
This skill owns
- React / Next.js performance triage for waterfalls, bundle weight, server-client boundaries, hydration/script cost, and rerender churn
- measurement-first audits of slow routes, heavy client components, and App Router performance regressions
- code-level React / Next.js performance refactors once the dominant bottleneck is clear
Route out to nearby skills
state-management
Use when the main problem is choosing where state should live or which store/query layer should own the data flow.
Examples:
- Context vs Zustand vs Redux Toolkit vs TanStack Query
- URL/form/local/shared/server-state ownership
- architectural state boundaries before performance tuning
web-accessibility
Use when the visible issue is keyboard/focus/semantics/aria/accessibility compliance.
Examples:
- focus traps, missing labels, screen-reader flow
- interactive semantics broken after a UI refactor
- accessibility verification is the primary ask
responsive-design
Use when the core task is layout adaptation, overflow, reflow, breakpoint/container logic, or mobile-first rendering behavior.
Examples:
- cards overflow on tablet
- layout collapses on small screens
- issue is primarily responsive adaptation, not route JS cost
performance-optimization
Use when the bottleneck is broader than React or spans backend, database, capacity, network, worker, or mixed runtime concerns.
Examples:
- slow API/database dominates the route
- memory, CPU, queue, or infra bottlenecks
- performance packet needs cross-stack tuning rather than React-specific guidance
Alias boundary
vercel-react-best-practices is a compatibility alias only. Use it when exact-name legacy tooling or user wording requires the old name. Ordinary React / Next.js performance requests should activate react-best-practices directly.
Practical rule
If you cannot name a specifically React / Next.js performance symptom, you probably need a different anchor first.
Measurement and tooling checklist
Use this file when the bottleneck is unclear or when you need evidence before recommending a refactor.
1. Choose the measurement surface
React subtree cost
Use when the complaint is rerender churn, expensive state updates, or interaction lag inside a known subtree.
- React DevTools Profiler /
<Profiler> - focus on which interactions are slow and which subtree re-renders repeatedly
- remember large apps can make profiling brittle; if the profiler freezes, fall back to broader browser traces
Source: <https://react.dev/reference/react/Profiler>
Route JS / dependency weight
Use when the page ships too much JavaScript or heavy components load on first render.
- Next bundle analyzer or equivalent bundle diff tooling
- compare before/after route weight and lazy-loaded splits
- if the team works through PRs, favor diff/budget tooling instead of one-off local screenshots
Sources:
- <https://nextjs.org/docs/14/pages/building-your-application/optimizing/bundle-analyzer>
- <https://github.com/hashicorp/nextjs-bundle-analysis>
- <https://nextjs.org/docs/app/guides/package-bundling>
User-facing page quality
Use when the symptom is poor LCP, CLS, INP, or a release readiness question.
- Core Web Vitals / field-vs-lab checks
- Next analytics or
useReportWebVitals - Speed Insights or another RUM surface when available
Sources:
- <https://web.dev/articles/vitals>
- <https://nextjs.org/docs/app/guides/analytics>
- <https://vercel.com/docs/speed-insights>
Hydration / script placement issues
Use when the issue is mismatch warnings, flicker, or degraded interaction after third-party scripts load.
- inspect script placement and loading strategy
- verify whether the problem is hydration, script cost, or a broader client-boundary mistake
Source: <https://nextjs.org/docs/app/guides/scripts>
2. Ask the minimum evidence questions
- Which route or interaction is slow?
- Is the complaint initial load, interaction lag, or dev-loop slowness?
- Do we already have profiler, CWV, or bundle evidence?
- Did the regression start after a routing, data-fetching, or third-party script change?
3. Pick the first tool, not every tool
- unknown symptom → CWV/Lighthouse or route-level reproduction first
- known large bundle → bundle analyzer first
- rerender suspicion → React Profiler first
- hydration/script warning → script and client-boundary review first
- mixed or inconclusive signal → browser performance trace after the first pass
4. Common real-world workflow
1. detect regression with CWV, Lighthouse, or user report 2. localize with React Profiler, bundle analyzer, or browser trace 3. classify into the triage modes 4. recommend the smallest high-impact fix first 5. re-measure after the fix instead of stacking speculative optimizations
5. Pitfalls
- Do not default to
memo/useCallbackbefore measuring. - Do not treat all slow routes as bundle problems; waterfalls and boundary mistakes often dominate.
- Do not trust a single profiler screenshot as complete truth on very large pages.
- Do not keep React-specific work when the real issue is accessibility, layout, API latency, or database/runtime pressure.
React / Next.js performance triage modes
Use this file to classify the request before you touch AGENTS.md.
1. Waterfalls / async sequencing
Use when the page waits on independent fetches, late-started async work, or nested Suspense boundaries that serialize unnecessarily.
Signals
- “slow because it waits on three API calls”
- server components or loaders start work too late
- route HTML or data is delayed by sequential awaits
First fixes
- start independent work early
- use
Promise.allfor truly independent fetches - move awaited work closer to the branch that needs it
- use Suspense for streaming instead of blocking the whole route
Primary references
AGENTS.mdwaterfall rules- <https://nextjs.org/docs/app/guides/lazy-loading>
2. Bundle weight / lazy loading
Use when too much JavaScript ships to the route, heavy components load eagerly, or third-party packages dominate the bundle.
Signals
- “bundle bloat”, “too much JS”, “dashboard route is huge”
- heavy editors/charts/modals loaded on first paint
- barrel imports or oversized icon/component packages
First fixes
- dynamic import heavy client components
- remove barrel imports and import narrower entry points
- defer third-party code until interaction or post-hydration
- re-check route JS with bundle analysis before and after
Primary references
- <https://nextjs.org/docs/app/guides/lazy-loading>
- <https://nextjs.org/docs/app/guides/package-bundling>
- <https://vercel.com/blog/how-we-optimized-package-imports-in-next-js>
3. RSC / server-client boundary mistakes
Use when the app pushes too much work to Client Components, serializes too much data, or confuses server-owned and client-owned logic.
Signals
- “client component too heavy”
- App Router route regressed after moving logic across the boundary
- lots of props or fetched data shipped only to support tiny client interactivity
First fixes
- move static / data-heavy work back to the server when possible
- minimize serialized props crossing the boundary
- isolate client-only instrumentation or interactivity into narrow islands
- avoid widening the client tree for analytics or convenience hooks
Primary references
AGENTS.mdserver rules- <https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals>
- <https://nextjs.org/docs/app/guides/analytics>
4. Rerender churn / memoization
Use when UI interactions feel sluggish because components, contexts, or callbacks trigger repeated render work.
Signals
- “rerenders too much”
- expensive child trees update when unrelated state changes
- context updates fan out across large subtrees
First fixes
- measure with React Profiler first
- reduce context spread and unnecessary parent updates
- use stable callback / memo patterns only where measurements justify them
- keep React Compiler in mind before prescribing blanket
memo/useCallback
Primary references
- <https://react.dev/reference/react/Profiler>
- <https://react.dev/reference/react/memo>
- <https://react.dev/reference/react/useCallback>
- <https://react.dev/learn/react-compiler>
5. Hydration / script cost / third-party side effects
Use when the page flashes, mismatches, or slows down because browser-only code, analytics, chat widgets, or script injection happen at the wrong time.
Signals
- hydration mismatch warnings
- slow interaction after third-party scripts load
- layout shifts or client-only values cause flicker
First fixes
- isolate browser-only logic carefully
- move or defer scripts with Next script guidance
- separate user-visible hydration problems from broader state or accessibility issues
- verify whether the real issue is third-party script cost, not React rerenders
Primary references
- <https://nextjs.org/docs/app/guides/scripts>
- <https://nextjs.org/docs/app/guides/analytics>
6. Measurement-first unknowns
Use when the request only says “the page is slow” or “this route feels bad” and the bottleneck is not obvious.
Default workflow 1. collect the visible symptom and route scope 2. choose one measurement surface from measurement-and-tooling-checklist.md 3. classify into one dominant packet above 4. only then recommend specific fixes
Route-outs
- app-wide client/server state ownership →
../state-management/SKILL.md - accessibility-specific failures →
../web-accessibility/SKILL.md - layout adaptation / overflow / reflow →
../responsive-design/SKILL.md - non-React-wide bottleneck or mixed backend/db/runtime issue →
../performance-optimization/SKILL.md
N:react-best-practices
D:Measurement-led React/Next.js performance audits for waterfalls, bundle size, RSC or client-boundary mistakes, hydration/script cost, rerender churn, and slow route interactions.
G:React Next.js performance optimization vercel waterfalls bundle-size RSC hydration profiler web-vitals frontend
U[5]:
Writing React components — avoid performance anti-patterns
Next.js data fetching — remove waterfalls, parallelize
Bundle size optimization — remove barrel imports, use dynamic imports
Code review — detect performance issues using the 45-rule set
Refactoring — optimize existing React/Next.js code
S[8]{n,action,details}:
1,Waterfalls(CRITICAL),Parallelize independent fetches with Promise.all(); stream with Suspense; defer await to where it's actually used
2,Bundle(CRITICAL),Prohibit barrel imports (use direct lucide-react paths); lazy-load heavy components with next/dynamic
3,Server Perf(HIGH),Deduplicate per-request with React.cache(); cache across requests with an LRU cache; run non-blocking work with after()
4,Client Fetch(MEDIUM-HIGH),Automatic request deduplication with SWR; deduplicate global event listeners
5,Re-render(MEDIUM),Stable callbacks via useCallback + functional setState; split expensive work into memoized components
6,Rendering(MEDIUM),Hoist static JSX outside components; optimize long lists with content-visibility
7,JS Perf(LOW-MEDIUM),O(1) lookups with Map/Set; cache object properties in loops; early return (early exit)
8,Advanced(LOW),Store event handlers in refs; useLatest for stable callback refs
R[6]:
Always parallelize independent fetches with Promise.all()
No barrel file imports — import from direct paths
Lazy-load heavy components with next/dynamic
Do not reference state directly in useCallback dependencies — use functional setState
Use toSorted() instead of sort() (preserves immutability)
No inline objects inside React.cache() (prevents cache misses)
E[4]{desc,in,out}:
"Eliminate waterfalls","const user = await fetchUser(); const posts = await fetchPosts();","const [user, posts] = await Promise.all([fetchUser(), fetchPosts()])"
"Barrel import optimization","import { Check, X, Menu } from 'lucide-react'","import Check from 'lucide-react/dist/esm/icons/check'"
"dynamic import","import { MonacoEditor } from './monaco-editor'","const MonacoEditor = dynamic(() => import('./monaco-editor').then(m => m.MonacoEditor), { ssr: false })"
"functional setState","setItems([...items, ...newItems]) — items dependency","setItems(curr => [...curr, ...newItems]) — empty dependency array"
Related skills
FAQ
What does it prioritize first?
Eliminating waterfalls and bundle size optimization are the CRITICAL top-priority categories.
Does it fix before measuring?
No, it classifies the primary bottleneck before suggesting fixes and uses a measurement-first approach.