react-performance-audit
Skill0 starsChanged 3 months agoMITView on GitHub
Audit React and Next.js codebases for wasted re-renders, bundle bloat, and Core Web Vitals regressions, then produce a prioritized fix list with measured impact. Use this skill whenever the user mentions slow renders, laggy typing or scrolling, large bundles, LCP/INP/CLS problems, Lighthouse scores, React DevTools Profiler output, memo/useMemo/useCallback decisions, or says something is "janky", "slow", or "re-rendering too much" — including when they just paste a component and ask why it feels slow.
In this repository5 other files it gives its agents.
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 5 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Before reading any code, get the symptom and the number.
- Very specific: line 23
If the user has no measurement, provide one before diagnosing: a Lighthouse run, a React DevTools Profiler recording, or npx …
- Distinctive: line 29
Run the bundled scanner over the source to collect candidate issues fast:
- Distinctive: line 40
Treat its output as leads, not verdicts.
- Very specific: line 77
Bundle weight: Check for moment, full lodash, unshaken icon packs, and duplicated transitive dependencies.
- Distinctive: line 87
Use this structure so the reader can act without reading everything:
- Distinctive: line 119
Every fix needs a before/after number from the same measurement used in Step 0.
- Very specific: line 129
Do not recommend wrapping everything in memo, do not suggest useCallback for handlers passed to plain DOM elements, and do not present a …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
React Performance Audit
Performance work goes wrong in two directions: optimizing what was never slow, and
sprinkling useMemo until the code is unreadable and no faster. This skill enforces the
measure → diagnose → fix → verify loop, and refuses to skip the first step.
Step 0 — Establish what "slow" means here
Before reading any code, get the symptom and the number. Ask which of these it is, because each has a different root cause family:
- Slow initial load → bundle size, server response, render-blocking resources → LCP
- Slow interaction / laggy typing → re-render cost, long tasks on the main thread → INP
- Layout shifting while loading → missing dimensions, late-injected content → CLS
- Slow list scrolling → per-row render cost, missing virtualization
- Slow navigation between routes → unsplit bundles, waterfall data fetching
If the user has no measurement, provide one before diagnosing: a Lighthouse run, a React
DevTools Profiler recording, or npx source-map-explorer on the build output. An audit
without a baseline cannot demonstrate improvement and usually optimizes the wrong thing.
Step 1 — Static scan
Run the bundled scanner over the source to collect candidate issues fast:
node scripts/perf_scan.mjs <src-dir>It flags high-signal patterns: inline object and arrow props passed to memoized children,
array index keys, useEffect with a missing or over-broad dependency array, state that
should be derived, context values constructed inline, heavy top-level imports, and
useState holding values that never trigger a render.
Treat its output as leads, not verdicts. The scanner cannot see how often a component renders or how expensive that render is, and a flagged pattern in a component that renders twice a session is not a problem worth a PR.
Step 2 — Diagnose against the real cost model
React re-renders a component when its state changes, its context value changes, or its parent re-renders. The last one is the source of most confusion — a parent re-render re-renders all children regardless of whether their props changed, unless the child is memoized and its props are referentially stable.
Work through these in order of typical impact:
Context that changes too often. A context whose value is a fresh object every render
invalidates every consumer in the tree. Split contexts by update frequency — a stable
dispatch-like context and a volatile state context — and memoize the value. This single
fix routinely removes more wasted renders than every React.memo in a codebase.
Uncontrolled subtree re-renders. Lifting state higher than needed makes the whole tree
re-render on every keystroke. Push state down to the smallest component that owns it, or
move the expensive siblings into children so they keep the same element identity across
parent renders.
Memoization that does nothing. React.memo on a component receiving style={{...}}
or onClick={() => ...} compares a new reference every time and always re-renders, having
added a comparison cost for no benefit. Either stabilize the props or drop the memo.
Over-memoization. useMemo on a cheap computation costs a dependency comparison plus
retained memory to save a few microseconds. Reserve it for expensive derivations, large
list transforms, and referential stability that something downstream actually depends on.
With the React Compiler enabled, most manual memoization becomes redundant — check for it
before adding more.
Expensive renders rather than frequent ones. Long lists without virtualization, chart libraries rendering thousands of DOM nodes, date formatting inside a map. Fix the cost per render, not the count.
Bundle weight. Check for moment, full lodash, unshaken icon packs, and duplicated
transitive dependencies. Route-level React.lazy and dynamic import() for anything below
the fold; next/dynamic with ssr: false for browser-only widgets.
Data waterfalls. A component fetching, then rendering a child that fetches, serializes round trips. Hoist to a parallel fetch, or in the App Router move the fetch into a Server Component and stream with Suspense.
Step 3 — Report
Use this structure so the reader can act without reading everything:
## Summary
One paragraph: what's slow, why, expected gain from the top three fixes.
## Baseline
| Metric | Current | Target |
|---|---|---|
| LCP / INP / CLS / bundle (gzip) | | |
## Findings
### 1. <Title> — Impact: High | Effort: Low
**Where:** `src/path/File.tsx:42`
**What happens:** the mechanism, in terms of React's render model
**Evidence:** profiler number, bundle bytes, or scanner hit
**Fix:** minimal diff
**Expected gain:** quantified, with the assumption stated
## Not worth fixing
Patterns that look wrong but cost nothing here — with the reason. This section keeps the
team from "fixing" things later and reintroducing complexity.
## Verification
The exact command or profiling step that confirms each fix landed.Order findings by impact-to-effort, never by file order. Cap the list at the eight that matter; a 40-item audit is ignored.
Step 4 — Verify and prevent
Every fix needs a before/after number from the same measurement used in Step 0. Then close the loop so the regression cannot silently return:
- Bundle budget in CI (
size-limitor@next/bundle-analyzerwith a threshold that fails) - Lighthouse CI on preview deployments with asserted budgets
- Real-user monitoring via
web-vitalsreporting INP and LCP at p75, since lab numbers on fast hardware hide the problem that users actually have
Anti-patterns to avoid in your own recommendations
Do not recommend wrapping everything in memo, do not suggest useCallback for handlers
passed to plain DOM elements, and do not present a micro-optimization as a headline fix
when a 400 KB dependency is sitting in the entry chunk. Credibility in a performance review
comes from correct prioritization more than from knowing obscure APIs.
For the mechanics of profiling — Profiler flame chart reading, why-did-you-render setup,
Chrome performance traces, and INP attribution — see references/measuring.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

