React Component Performance
Upstream source: Dimillian/Skills (MIT) · packaged via antigravity-awesome-skills (MIT)
Overview
Identify render hotspots, isolate expensive updates, and apply targeted optimizations without changing UI behavior.
When to Use
- When the user asks to profile or improve a slow React component.
- When you need to reduce re-renders, list lag, or expensive render work in React UI.
Workflow
- Reproduce or describe the slowdown.
- Identify what triggers re-renders (state updates, props churn, effects).
- Isolate fast-changing state from heavy subtrees.
- Stabilize props and handlers; memoize where it pays off.
- Reduce expensive work (computation, DOM size, list length).
- Validate: open React DevTools Profiler → record the interaction → inspect the Flamegraph for components rendering longer than ~16 ms → compare against a pre-optimization baseline recording.
Checklist
- Measure: use React DevTools Profiler or log renders; capture baseline.
- Find churn: identify state updated on a timer, scroll, input, or animation.
- Split: move ticking state into a child; keep heavy lists static.
- Memoize: wrap leaf rows with
memo only when props are stable.
- Stabilize props: use
useCallback/useMemo for handlers and derived values.
- Avoid derived work in render: precompute, or compute inside memoized helpers.
- Control list size: window/virtualize long lists; avoid rendering hidden items.
- Keys: ensure stable keys; avoid index when order can change.
- Effects: verify dependency arrays; avoid effects that re-run on every render.
- Style/layout: watch for expensive layout thrash or large Markdown/diff renders.
Optimization Patterns
Isolate ticking state
Move a timer or animation counter into a child so the parent list never re-renders on each tick.
// ❌ Before – entire parent (and list) re-renders every second
function Dashboard({ items }: { items: Item[] }) {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return (
<>
<Clock tick={tick} />
<ExpensiveList items={items} /> {/* re-renders every second */}
</>
);
}
// ✅ After – only <Clock> re-renders; list is untouched
function Clock() {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return <span>{tick}s</span>;
}
function Dashboard({ items }: { items: Item[] }) {
return (
<>
<Clock />
<ExpensiveList items={items} />
</>
);
}
Stabilize callbacks with useCallback + memo
// ❌ Before – new handler reference on every render busts Row memo
function List({ items }: { items: Item[] }) {
const handleClick = (id: string) => console.log(id); // new ref each render
return items.map(item => <Row key={item.id} item={item} />);
}
// ✅ After – stable handler; Row only re-renders when its own item changes
const Row = memo(({ item, onClick }: RowProps) => (
<li => onClick(item.id)}>{item.name}</li>
));
function List({ items }: { items: Item[] }) {
const handleClick = useCallback((id: string) => console.log(id), []);
return items.map(item => <Row key={item.id} item={item} />);
}
Prefer derived data outside render
// ❌ Before – recomputes on every render
function Summary({ orders }: { orders: Order[] }) {
const total = orders.reduce((sum, o) => sum + o.amount, 0); // runs every render
return <p>Total: {total}</p>;
}
// ✅ After – recomputes only when orders changes
function Summary({ orders }: { orders: Order[] }) {
const total = useMemo(() => orders.reduce((sum, o) => sum + o.amount, 0), [orders]);
return <p>Total: {total}</p>;
}
Additional patterns
- Split rows: extract list rows into memoized components with narrow props.
- Defer heavy rendering: lazy-render or collapse expensive content until expanded.
Profiling Validation Steps
- Open React DevTools → Profiler tab.
- Click Record, perform the slow interaction, then Stop.
- Switch to Flamegraph view; any bar labeled with a component and time > ~16 ms is a candidate.
- Use Ranked chart to sort by self render time and target the top offenders.
- Apply one optimization at a time, re-record, and compare render counts and durations against the baseline.
Example Reference
Load references/examples.md when the user wants a concrete refactor example.
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
1---2name: react-component-performance3description: Diagnose slow React components and apply targeted fixes — isolate ticking state, stabilize props/handlers, memoize leaves, virtualize lists, verify with the Profiler. USE WHEN a React component is slow, janky, or re-rendering too much.4---56# React Component Performance78> Upstream source: Dimillian/Skills (MIT) · packaged via antigravity-awesome-skills (MIT)91011## Overview1213Identify render hotspots, isolate expensive updates, and apply targeted optimizations without changing UI behavior.1415## When to Use16- When the user asks to profile or improve a slow React component.17- When you need to reduce re-renders, list lag, or expensive render work in React UI.1819## Workflow20211. Reproduce or describe the slowdown.222. Identify what triggers re-renders (state updates, props churn, effects).233. Isolate fast-changing state from heavy subtrees.244. Stabilize props and handlers; memoize where it pays off.255. Reduce expensive work (computation, DOM size, list length).266. **Validate**: open React DevTools Profiler → record the interaction → inspect the Flamegraph for components rendering longer than ~16 ms → compare against a pre-optimization baseline recording.2728## Checklist2930- Measure: use React DevTools Profiler or log renders; capture baseline.31- Find churn: identify state updated on a timer, scroll, input, or animation.32- Split: move ticking state into a child; keep heavy lists static.33- Memoize: wrap leaf rows with `memo` only when props are stable.34- Stabilize props: use `useCallback`/`useMemo` for handlers and derived values.35- Avoid derived work in render: precompute, or compute inside memoized helpers.36- Control list size: window/virtualize long lists; avoid rendering hidden items.37- Keys: ensure stable keys; avoid index when order can change.38- Effects: verify dependency arrays; avoid effects that re-run on every render.39- Style/layout: watch for expensive layout thrash or large Markdown/diff renders.4041## Optimization Patterns4243### Isolate ticking state4445Move a timer or animation counter into a child so the parent list never re-renders on each tick.4647```tsx48// ❌ Before – entire parent (and list) re-renders every second49function Dashboard({ items }: { items: Item[] }) {50 const [tick, setTick] = useState(0);51 useEffect(() => {52 const id = setInterval(() => setTick(t => t + 1), 1000);53 return () => clearInterval(id);54 }, []);55 return (56 <>57 <Clock tick={tick} />58 <ExpensiveList items={items} /> {/* re-renders every second */}59 </>60 );61}6263// ✅ After – only <Clock> re-renders; list is untouched64function Clock() {65 const [tick, setTick] = useState(0);66 useEffect(() => {67 const id = setInterval(() => setTick(t => t + 1), 1000);68 return () => clearInterval(id);69 }, []);70 return <span>{tick}s</span>;71}7273function Dashboard({ items }: { items: Item[] }) {74 return (75 <>76 <Clock />77 <ExpensiveList items={items} />78 </>79 );80}81```8283### Stabilize callbacks with `useCallback` + `memo`8485```tsx86// ❌ Before – new handler reference on every render busts Row memo87function List({ items }: { items: Item[] }) {88 const handleClick = (id: string) => console.log(id); // new ref each render89 return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);90}9192// ✅ After – stable handler; Row only re-renders when its own item changes93const Row = memo(({ item, onClick }: RowProps) => (94 <li onClick={() => onClick(item.id)}>{item.name}</li>95));9697function List({ items }: { items: Item[] }) {98 const handleClick = useCallback((id: string) => console.log(id), []);99 return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);100}101```102103### Prefer derived data outside render104105```tsx106// ❌ Before – recomputes on every render107function Summary({ orders }: { orders: Order[] }) {108 const total = orders.reduce((sum, o) => sum + o.amount, 0); // runs every render109 return <p>Total: {total}</p>;110}111112// ✅ After – recomputes only when orders changes113function Summary({ orders }: { orders: Order[] }) {114 const total = useMemo(() => orders.reduce((sum, o) => sum + o.amount, 0), [orders]);115 return <p>Total: {total}</p>;116}117```118119### Additional patterns120121- **Split rows**: extract list rows into memoized components with narrow props.122- **Defer heavy rendering**: lazy-render or collapse expensive content until expanded.123124## Profiling Validation Steps1251261. Open **React DevTools → Profiler** tab.1272. Click **Record**, perform the slow interaction, then **Stop**.1283. Switch to **Flamegraph** view; any bar labeled with a component and time > ~16 ms is a candidate.1294. Use **Ranked chart** to sort by self render time and target the top offenders.1305. Apply one optimization at a time, re-record, and compare render counts and durations against the baseline.131132## Example Reference133134Load `references/examples.md` when the user wants a concrete refactor example.135136## Limitations137- Use this skill only when the task clearly matches the scope described above.138- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.139- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.