why-did-you-render

Monkey-patches React to flag avoidable re-renders caused by unchanged props, state, or hooks.

Library
npm
v10.0.1
12,519stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
53/100Fair
Development Activity8
Maintenance44
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture80
Code Quality78
Innovation82
Learning Curve55

@welldone-software/why-did-you-render patches React’s createElement, cloneElement, and core hooks to watch every component render and report when that render was technically avoidable — when props, state, or hook results were deeply equal to the previous render but still triggered a re-render because of referential inequality, such as a new object or function literal created on every parent render. It works with class components, function components, memoized components, and forwardRef components, and can track custom hooks such as react-redux’s useSelector.

Because the library hooks directly into React’s element-creation and hook APIs rather than relying on the Profiler API, it can pinpoint the exact prop, state, or hook path that changed and print a readable console diff of the before/after values. It’s meant strictly for local development — the README explicitly warns against production use, since the patching adds overhead and can produce unexpected behavior — and it supports React Native as well as web React.

What You Get

  • Console warnings showing exactly which prop, state, or hook value changed (or didn’t) on every avoidable re-render, including a readable diff of before/after values.
  • Support for all four React component shapes — class components, function components, React.memo, and forwardRef — through dedicated patch modules.
  • Hook-level tracking for React’s built-in useState, useReducer, useContext, and useMemo/useCallback, plus the ability to track custom hooks like react-redux’s useSelector via trackExtraHooks.
  • Configurable notifier, include/exclude regex filters, and per-component opt-in via a static whyDidYouRender flag, so tracking can be scoped to just the components you’re debugging.

Common Use Cases

  • Diagnosing why a big memoized list or table component keeps re-rendering despite using React.memo or PureComponent.
  • Finding call sites that pass new object/array/function literals as props on every parent render, defeating memoization.
  • Auditing a component tree after introducing a custom hook like react-redux’s useSelector to confirm it isn’t causing extra hook-driven re-renders.
  • Verifying the effect of a performance optimization (memoization, useCallback, useMemo) by confirming avoidable re-renders drop to zero.

Under The Hood

Architecture whyDidYouRender (src/whyDidYouRender.js) monkey-patches React.createElement, React.createFactory, and React.cloneElement so every element creation for a supported component type is routed through a tracking layer; getWDYRType decides whether a given component type should be tracked (via shouldTrack) and dispatches to one of four dedicated patch modules under src/patches/ — patchClassComponent, patchFunctionalOrStrComponent, patchMemoComponent, and patchForwardRefComponent — each wrapping the component so its render is compared against the previous one via getUpdateInfo and calculateDeepEqualDiffs. Shared mutable state (the original React methods, per-run hook info, per-component patched wrappers) lives in a single wdyrStore singleton, and trackHooksIfNeeded separately rewrites React’s useState/useReducer/useContext/useSyncExternalStore/useMemo/useCallback (plus any hooks passed via trackExtraHooks) to funnel hook-level changes through the same reporting path. The whole patch is reversible at runtime through an injected React.REVERT_WHY_DID_YOU_RENDER function.

Tech Stack The library is authored in plain JavaScript (97% of the codebase) with hand-written TypeScript declaration files (types.d.ts, jsx-runtime.d.ts) for consumers, targeting React 19 as a peer dependency and using lodash as its sole runtime dependency for small utilities like get/isFunction. It’s built with Babel (@babel/preset-env, @babel/preset-react) and bundled with Rollup into a single dist/whyDidYouRender.js CommonJS entry point, with Nollup powering a live-reloading demo app served via Express (demo/serve.js) for manual testing against React, React Redux, and React Router.

Code Quality The tests/ directory mirrors src/ file-for-file, with dedicated Jest specs for calculateDeepEqualDiffs, defaultNotifier, findObjectsDifferences, getDisplayName, getUpdateInfo, normalizeOptions, and shouldTrack, plus separate suites for hook tracking, strict-mode behavior, and a librariesTests/ folder covering integration with libraries like react-redux — an extensive test surface for a project this size. End-to-end coverage is added via Cypress against the demo app. Linting runs through a flat ESLint config with the React and Cypress plugins and --max-warnings 0 enforced in CI, though the library itself is untyped JavaScript rather than compiled TypeScript, so type safety for internal code relies on convention and the hand-maintained .d.ts files rather than compiler enforcement.

What Makes It Unique Rather than surfacing timing data the way React’s built-in Profiler API does, why-did-you-render patches React’s own element-creation and hook internals to do structural, path-level diffing of props, state, and hook results, letting it tell you specifically whether a re-render was avoidable (deeply equal inputs, different reference) versus a legitimate change — and to extend that same diffing to arbitrary custom hooks like a Redux selector via trackExtraHooks. That focus on causal, per-value diagnosis rather than aggregate timing is a distinctive technical choice for a React performance-debugging tool.

Used by 6 apps in this directory

TypeScript
68%
Apache 2.0

Appsmith

Developer Tools · Automation · No Code Platforms

40,825

Open-source low-code platform to build admin panels, dashboards, and internal tools connected to any database or API.

View details
93
Repo Health
79
Technical
66
Dependency
Built with
TypeScript68%
Java21%
Updated 3 days ago
JavaScript
100%
Other

Automatisch

Automation · No Code Platforms

13,964

Self-hosted, no-code workflow automation that keeps your data on your own servers—a privacy-first alternative to Zapier with 90+ integrations.

View details
49
Repo Health
78
Technical
63
Dependency
Built with
JavaScript100%
Updated 6 months ago
TypeScript
97%
AGPL 3.0

Bigcapital

Invoicing Finance

3,884

Self-hostable double-entry accounting platform with invoicing, inventory, multi-currency, and real-time financial reporting for small and medium businesses.

View details
91
Repo Health
77
Technical
61
Dependency
Built with
TypeScript97%
Updated yesterday
TypeScript
49%
AGPL 3.0

Grafana

Monitoring · Analytics

76,643

The open-source observability platform that unifies metrics, logs, and traces from any data source into dynamic, queryable dashboards.

View details
95
Repo Health
91
Technical
63
Dependency
Built with
TypeScript49%
Go45%
Updated today
Python
65%
MIT

LibrePhotos

File Storage

8,061

Self-hosted photo library with AI-powered face recognition, semantic search, and automatic event albums — no cloud required.

View details
82
Repo Health
78
Technical
66
Dependency
Built with
Python65%
TypeScript32%
Updated 2 days ago
JavaScript
75%
AGPL 3.0

Twake Drive

File Storage · Collaboration

981

Open-source, self-hosted file manager with collaborative drives, AI-powered summarization, and OnlyOffice document editing for teams who want full data sovereignty.

View details
90
Repo Health
78
Technical
72
Dependency
Built with
JavaScript75%
TypeScript23%
Updated 3 days ago

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search