react-render-detective

Know why your React components render.

Not just that they did. The changed value, where it came from, what it cost, and how sure we are.

12.4 KB gzip, everything loaded zero runtime dependencies no private React internals nothing leaves your machine

The difference

Every render tool can count renders. Counting is not the hard part.

What you usually get

UserProfile rendered 47 times.

What you actually need

UserProfile rendered because `user`
changed by reference.
Its values are identical.
Dashboard recreated the object.

Install

npm install react-render-detective
// once, at your entry point
import { init } from "react-render-detective";
if (process.env.NODE_ENV !== "production") init();

// then wrap whatever you suspect
import { withRenderDetective } from "react-render-detective";
const UserProfile = withRenderDetective(function UserProfile({ user, onSave }) {
  return /* … */;
});

No server, no account, no API key, no browser extension.

 [RRD] UserProfile #47  Reason: prop changed (reference only)  Changed: user  Duration: 8.4ms

Ask for the whole story

explain() aggregates every recorded render of one component into one answer.

> rrd.explain("ProductRow")

ProductRow

143 recorded renders

Why?
  87% of renders followed `filters` changing by reference while its contents stayed the same.

Breakdown
  props                 124  87%
  parent                 15  10%
  mount                   1   1%
  state-or-external       3   2%

Reference-only prop changes
  filters               124  87%  (object)
  onSelect              118  83%  (function)

Cost
  average       12.4ms
  total         1773.2ms
  potentially avoidable  124 render(s), ~1537.6ms

Next step
  Find where `filters` is created in ProductPage and stabilise it
  (useMemo, or pass the primitive fields you use).

Confidence: high

Three rules it holds to

Rendering is not a bug Output says render, potentially avoidable render, slow render. Never “BAD RENDER”. Most renders are fine.
Memoization is a trade React.memo is suggested only when the evidence supports it, with the cost shown so you can decide. Never blanket advice.
StrictMode is not a 2× regression Double-invoked renders are labelled development replays and excluded from every statistic.
When the runtime genuinely cannot tell you why something rendered, it says Cause could not be determined reliably. — rather than inventing a cause. Every diagnosis carries high / medium / low confidence, and the limitation behind it is printed with it.

What it can tell you

QuestionAnswer
What rendered?component, render number, mount vs update
Why?props · parent · context · state · external store — with the evidence
What changed?per prop: a real value change vs a reference-only change
Where from?the nearest instrumented ancestor, and whether it re-rendered
How expensive?subtree duration, and self duration with instrumented descendants subtracted
How sure are we?high / medium / low, on every single diagnosis
What next?an evidence-based suggestion — or nothing at all

Cost

Measured against the built package. Full method and caveats in BENCHMARKS.md.

Instrumented share of a 2 000-component treeBaselineInstrumentedOverhead
2%35.02 ms36.89 ms+5.3%
10%36.22 ms38.34 ms+5.9%
50%35.55 ms49.42 ms+39.0%

Roughly 10 µs per instrumented component, flat with tree size — against the 0.1 ms target, that is about 8× headroom. Percentage overhead is a function of how many components you wrap and how much work each does: wrapping every trivial leaf in a big tree is expensive, and the benchmark says so rather than quoting only the flattering number.

Honest limitations

This package uses no private React APIs. That costs it some things, and every one of them is written down.

The full feasibility report classifies every feature as reliable, inferred, or impossible without internals — written before any of it was built.

Status

0.7.0 — early release. 76 tests pass on React 18 and 19; benchmarks and bundle budgets are green; the packed package is verified in a clean install for ESM, CJS and TypeScript consumers; and the demo dashboard has been driven end to end in Chrome, which found four real defects the jsdom suite had missed. Not yet exercised: Suspense and error-boundary edges, and any app that isn't the demo. Treat it as a preview and please report what it gets wrong.