Aller au contenu principal

Skill: dev-react-perf

Inline

React/Next.js performance optimization. Points to Vercel's react-best-practices (70 rules by impact - waterfalls, bundle, re-renders) and adds what it leaves out - list virtualization, state colocation, profiling tools, Core Web Vitals targets. Trigger when the user wants to optimize rendering, reduce re-renders, or improve Core Web Vitals.

Configuration​

PropertyValue
Contextinline
Pre-approved toolsNone declared: the project's permission settings apply
Triggerkeywords: dev, react, perf, vercel —

Detailed description​

React Performance (pointer + gaps)

If the vercel-react-best-practices skill is installed, invoke it now (Skill tool) and apply its rules first. Either way, check these before anything else — the first two are the vendor's CRITICAL categories, the last three the re-render traps this foundation sees most:

  1. Request waterfalls (vendor, CRITICAL) — independent requests awaited one after another: start them together (Promise.all), await late, stream with Suspense.
  2. Bundle size (vendor, CRITICAL) — whole-library or barrel imports (import { x } from 'lodash'): import the path; load rarely used heavy UI (modals, charts, editors) with lazy() / next/dynamic.
  3. Derived state (vendor, re-render category) — never copy props into state through useEffect + setState: compute during render, useMemo if expensive.
  4. Memo that cannot work (foundation) — a memo child receiving inline arrows or inline objects re-renders anyway: pass stable callbacks and hoisted constants.
  5. Context values (foundation) — a provider value={{ ... }} built on every render re-renders every consumer: memoize it or split the context.

With the React Compiler enabled, items 4 and 5 are handled for you: it memoizes components and values itself (the vendor skill says the same of memo/useMemo).

Vercel Engineering publishes the canonical rule set at vercel-labs/agent-skills/skills/react-best-practices (MIT): 70 rules in 8 categories ranked by impact, starting with the two CRITICAL ones — eliminating request waterfalls and bundle size — then server/client data fetching, re-renders, rendering and JS micro-optimizations. Its companion composition-patterns covers compound components and boolean-prop explosion.

Why a pointer: the vendor maintains its rules with each React and Next.js release; this skill's former 211-line version had gone stale (FID, retired in 2024; a react-window API removed in v2). Measurements behind this change: eval/skill-triggering/FINDINGS.md.

Delegate to the vendor skill​

git clone --depth 1 https://github.com/vercel-labs/agent-skills ~/dev/vendor-skills/vercel
ln -s ~/dev/vendor-skills/vercel/skills/react-best-practices ./.claude/skills/react-best-practices
ln -s ~/dev/vendor-skills/vercel/skills/composition-patterns ./.claude/skills/composition-patterns

Recipe entry: docs/recipes/recommended-vendor-skills.md §"Vercel — vercel-labs/agent-skills".

What the vendor skill leaves out​

Virtualization, state colocation and the profiling tools below appear in none of its 72 files (it does cover content-visibility for long lists, a lighter alternative).

Long lists: virtualize​

Mounting thousands of rows costs more than any memoization saves. Render only the visible window:

import { useRef } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';

export function VirtualList({ items }: { items: { id: number; name: string }[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const rows = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 48,
});
return (
<div ref={parentRef} style={{ height: 400, overflow: 'auto' }}>
<div style={{ height: rows.getTotalSize(), position: 'relative' }}>
{rows.getVirtualItems().map((row) => (
<div
key={items[row.index].id}
style={{ position: 'absolute', top: 0, width: '100%', height: row.size, transform: `translateY(${row.start}px)` }}
>
{items[row.index].name}
</div>
))}
</div>
</div>
);
}

Rows need a stable key (an id, never the index) and a known or estimated height. Under a few hundred rows, pagination or content-visibility: auto is often enough.

State colocation (push state down)​

// BAD: state in the parent (re-renders everything)
function Page() {
const [search, setSearch] = useState('');
return (
<div>
<SearchBar value={search} onChange={setSearch} />
<ExpensiveList /> {/* Unnecessary re-render! */}
<Footer /> {/* Unnecessary re-render! */}
</div>
);
}

// GOOD: state in the component that uses it
function Page() {
return (
<div>
<SearchSection /> {/* Internal state */}
<ExpensiveList /> {/* Not affected */}
<Footer /> {/* Not affected */}
</div>
);
}

Typical case: a clock, a timer or a search input whose state sits at the app root re-renders the whole tree on every tick or keystroke. Move that state into the small component that displays it.

When the state cannot move down (a provider, a layout that owns it), lift the expensive content up instead: pass it as children. Elements created by the parent are not re-created when the wrapper's state changes, so they do not re-render.

Core Web Vitals targets​

MetricGoodNeeds workPoor
LCP< 2.5s2.5–4s> 4s
INP< 200ms200–500ms> 500ms
CLS< 0.10.1–0.25> 0.25

Measure before and after a change; the qa-perf skill holds the measurement workflow.

Profiling tools​

npx lighthouse https://example.com --view # lab LCP / INP proxy (TBT) / CLS
ANALYZE=true npm run build # bundle composition (Next.js, with @next/bundle-analyzer wired in next.config)
  • React DevTools Profiler — which components rendered, why, and for how long.
  • why-did-you-render (@welldone-software/why-did-you-render) — logs avoidable re-renders in development.

Automatic triggering​

This skill is automatically activated when:

  • The matching keywords are detected in the conversation
  • The task context matches the skill's domain

Triggering examples​

  • "I want to dev..."
  • "I want to react..."
  • "I want to perf..."

Context inline​

Inline means the skill runs in the conversation (the default, no context key):

  • Sees the full history
  • Its work is visible as it happens
  • Right for almost every skill

See also​