claude/ai-resources-plugin/skills/react-scan/SKILL.md
Install and use react-scan tooling for component-level React render attribution: "set up react-scan", "diagnose React re-renders", "find unnecessary renders", "find unstable props", "measure component renders programmatically", or automated render checks with Playwright (react-scan + playwright, lite headless API for CI). Covers install across Next.js/Vite/Remix/script-tag/browser-extension and the render-attribution → fix → validate loop. For trace-based triage of LCP/INP regressions, ForcedReflow, bundle bloat, or memory leaks, use investigate-web-perf instead.
npx skillsauth add amhuppert/my-ai-resources react-scanInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
react-scan instruments React's fiber tree via the same DevTools hook React DevTools uses, observing every commit without any application code change. It reports which component rendered, how long it took, and what changed (props / state / context / hooks / parent cascade). Pair it with Playwright and the result is a deterministic, file:line-attributable workflow for re-render bugs that traces alone cannot resolve.
This skill is the entry point. Detailed setup, integration patterns, API surface, diagnosis classification, and fix recipes live in references/ and load only when needed.
Use react-scan when the symptom points at component-level rendering, not asset delivery or paint:
<Results>")Do not use react-scan as the first move for: LCP regressions, network/bundle issues, layout shift, third-party scripts. Those belong in investigate-web-perf (sibling skill in this plugin). Trace-level analysis with the chrome-devtools-mcp Performance Insights complements react-scan: investigate-web-perf is canonical for trace work, react-scan is canonical for render-count attribution.
| Mode | When | How |
|---|---|---|
| Toolbar overlay (default) | Local dev, human-driven visual debugging | npx react-scan@latest init, or CLI proxy, or script tag |
| Lite (react-scan/lite) | Programmatic data collection, CI, agent-driven analysis | instrument({ onEvent }) or instrument({ endpoint }) |
| CLI proxy (npx react-scan@latest URL) | Quick check of any running app without touching its code | npx react-scan@latest http://localhost:3000 |
| Browser extension | Audit a deployed app you do not control | Install for Chrome/Firefox, click icon |
| Playwright + injection | Headless render counting in tests | Inject auto.global.js + patch onRender (see references/playwright-integration.md) |
For agent-driven workflows the order of preference is:
LiteEvent records).window.__REACT_SCAN__.ReactScanInternals is present.onRender(fiber, renders) callback, lite subscribe() events, or Store.reportData per-fiber map. Save raw payload to disk; do not stream to model context.changeDescription (props / state / context / hooks / parent). Cross-reference with source to find the unstable reference. Categories in references/diagnosis-patterns.md.references/fix-recipes.md). Do not bulk-add React.memo.dangerouslyForceRunInProduction: true is set. In Playwright against a dev server this works automatically; against any other build, set the flag or you will measure nothing and conclude wrongly. "Production build" here means React's production build as detected by bippy's detectReactBuildType — not "minified component names." A dev-mode React build can still emit minified single-letter names (e.g., Next.js + Turbopack); counters will work, names will just be unhelpful. If counters stay at zero, suspect prod React; if counters populate but names are _/r/i, you have a minified dev build — fix names via @react-scan/vite-plugin-react-scan autoDisplayNames: true (Vite) or babel-plugin-react-display-name (others) rather than reaching for the prod flag.React.memo without a measured render count first. Memoization has a non-zero cost; on components that render in <0.1 ms it's net negative. Always measure before and after.react-scan/lite for programmatic capture, not the full bundle. Lite has no overlay, no Preact, no toolbar — bundle and runtime overhead are minimal.LiteEvent arrays into the model context.react-scan/lite keepalive POSTs cap at ~64 KB. If using the endpoint option for large trees, tune maxFibersPerCommit (default 5000) downward or use in-process subscribe() instead.references/setup.mdreferences/playwright-integration.md + scripts/playwright-render-collector.tswindow?" → references/api-surface.mdreferences/diagnosis-patterns.md (load references/api-surface.md first if you don't yet have a render payload)references/fix-recipes.mdreferences/workflow.mdonRender output during JSX render. The callback fires on every commit; logging on every fire pollutes the console and skews measurements via console overhead. Buffer, then flush after the interaction completes.onRender calls per component. A single commit can produce one Render[] array with multiple entries — count the array length, not the callback invocations.react-scan in a Playwright test. The overlay attempts to mount Shadow DOM + Preact, which adds noise to render counts and slows tests. Use react-scan/lite or full bundle with showToolbar: false.Load on demand:
references/setup.md — Install per framework (Next.js App + Pages, Vite, Remix, script tag, CLI init, Vite plugin, browser extension, lite mode); environment guards and dev/prod gating.references/playwright-integration.md — addInitScript injection, the render-counter pattern from react-scan's own E2E suite, lite-mode subscriber, multi-page metrics aggregation, CI gating with budgets, when to disable StrictMode for measurement.references/api-surface.md — window.__REACT_SCAN__, ReactScanInternals, Store.reportData, onRender(fiber, renders) shape, Render / ChangeDescription types, the lite LiteEvent taxonomy and LiteOptions.references/diagnosis-patterns.md — Classification of root causes from changeDescription: unstable props, context over-scope, cascade from parent, memo bypass, hook dependency unstable, store selector instability, child-of-list-without-key.references/fix-recipes.md — Minimal-diff fixes per category: useMemo / useCallback placement, React.memo with custom equality, context split, selector with shallow, useDeferredValue / useTransition, list virtualization, key stabilization, lifting state down.references/workflow.md — End-to-end loop with prompts: reproduce → instrument → capture → classify → fix → validate, plus the slow-interaction (>150 ms long-render frame) and INP-driven variants.scripts/inject-react-scan.js — Playwright addInitScript-compatible helper that loads auto.global.js and disables the overlay for headless measurement.scripts/playwright-render-collector.ts — Example Playwright test that drives an interaction, collects per-component render counts via the lite subscriber, and writes a JSON report to disk for diff comparison.development
Debug a running web app via the web-debugger SDK: app logs, application state, runtime snapshots, React state, query cache.
development
Thoroughly understand a software development objective before implementation: research, identify ambiguities, ask clarifying questions. Use before starting implementation of a non-trivial or ambiguously specified feature, or when requirements leave open design decisions.
development
Locate the on-disk Claude Code transcript file (.jsonl under ~/.claude/projects/) for the current or a specified conversation.
development
Reflect on codebase navigation effectiveness at end of conversation. Surfaces dead ends, inefficiencies, missing context. Does not write files — pair with /kiro:steering-custom to persist.