Back to Projects
React Perf Profiler — DevTools Extension for React Performance Engineering
A Chrome and Firefox DevTools extension that hooks directly into __REACT_DEVTOOLS_GLOBAL_HOOK__ to profile React component render behavior at scale — detecting wasted renders with fiber bitmask decoding, quantifying memoization effectiveness, parsing RSC wire payloads, and predicting render times with TensorFlow.js. All heavy analysis is offloaded to three dedicated Web Workers, keeping the DevTools panel at 60fps across component trees with 10,000+ nodes.
## Overview
React DevTools tells you *that* a component re-rendered. **React Perf Profiler** tells you *why*, *how often*, *what it cost*, and *exactly what to change*. It hooks directly into `__REACT_DEVTOOLS_GLOBAL_HOOK__` — the same internal mechanism React DevTools itself uses — to intercept every fiber commit without requiring any instrumentation code in the target application. This means it works on production builds, third-party apps, and apps you cannot modify.
The tool is designed for performance engineers working at scale: apps with hundreds of components, teams maintaining strict render budgets in CI, and engineers who need to understand React Server Component payloads in production.
---
## Architecture
```
Page (target React app)
↕ __REACT_DEVTOOLS_GLOBAL_HOOK__ bridge (injected by content script)
Content Script (isolated world)
↕ chrome.runtime messages
Background Service Worker (lifecycle, tab management, cross-tab state)
↕
DevTools Panel (React 18 UI — the profiler interface)
↕ Web Workers ×3 (analysis · RSC analysis · timeline)
↕ IndexedDB (profile session persistence across DevTools close/open)
```
**State management:** A 1,178-line Zustand store (`profilerStore.ts`) holds a custom O(1) LRU cache for component data, a CircularBuffer for commit history (capacity 500), RSC payload tracking (FIFO, max 100), and an auto-analysis timer that fires 500ms after recording stops.
---
## Key Technical Achievements
### React Fiber Bitmask Decoding
React does not expose a public API to ask "did this component re-render due to a context change?" The information is encoded in the `fiber.flags` integer. The wasted render analyzer reads:
```typescript
const hasContextChange = fiber.flags & (0x040 | 0x1000);
// 0x040 = React 17 context flag
// 0x1000 = React 18 context flag
// Combined mask works across both versions
```
This, combined with shallow equality comparison of `prevProps` and `prevState` across commits, produces a precise classification of every wasted render: `parent-render`, `context-change`, `force-update`, or `unknown` — each with a specific, actionable recommendation (`React.memo`, `useCallback`, `useMemo`, state colocation).
### Three-Worker Off-Thread Analysis
The DevTools panel spawns three Web Workers on load, managed by a singleton `workerClient.ts` (781 lines). Each worker receives a typed request and returns a typed response correlated by a UUID key stored in a `Map`. Both `onerror` and `onmessageerror` handlers reject all pending requests for the failed worker, preventing silent hangs. This architecture means a 10,000-node component tree analysis never causes a single dropped frame in the panel UI.
### Custom O(1) LRU Cache
Component data is stored in a `ComponentDataLRUCache` class backed by a single `Map`. Because JavaScript's `Map` preserves insertion order:
- **Get:** delete the entry and re-insert it to mark it as recently used — O(1)
- **Evict:** `cache.keys().next().value` returns the oldest key — O(1)
No dependency on an external LRU library. The same principle is applied to the `CircularBuffer` for commit history: fixed-capacity array with a rotating write pointer, O(1) append, bounded memory regardless of recording duration.
### RSC Wire-Format Parser
A dedicated `rscParser.ts` module decodes the React Server Components wire format to track:
- Server/client boundary crossings
- Props payload sizes per crossing
- Cache hit/miss rates per boundary
- Serialization cost estimates
This runs in its own Web Worker (`rscAnalysis.worker.ts`) and surfaces the data in a dedicated panel tab — addressing a visibility gap in the official React DevTools.
### TensorFlow.js Render-Time Prediction
A `src/panel/utils/prediction/` module uses TensorFlow.js to predict component render times from historical commit data. TensorFlow.js is loaded via a **dynamic import** (`import()`) rather than statically bundled — it only loads when the prediction tab is opened, keeping the DevTools panel cold-start fast.
### CI Performance Budget CLI
The `perf-check` CLI binary (`src/cli/perf-check.ts`) provides a programmatic API:
```bash
npx perf-check --budget perf-budget.json --report profile.json
```
It validates render time budgets per component, per-chunk bundle sizes, and test coverage thresholds. On failure it generates a Markdown summary formatted as a GitHub PR comment and exits with code 1 (budget violation) or 2 (runtime error). Teams can integrate this into any CI pipeline to enforce performance standards on every pull request.
---
## Tech Stack
| Layer | Technologies |
|---|---|
| Extension platform | Chrome MV3 · Firefox MV2 |
| UI | React 18 · TypeScript · D3.js · Three.js · React Three Fiber |
| State | Zustand · Immer · Reselect |
| ML | TensorFlow.js (dynamic import) |
| Off-thread | Web Workers ×3 · IndexedDB |
| Build | Vite · CRXJS · dual manifests (Chrome + Firefox) |
| Testing | Vitest (≥75% coverage enforced) · Playwright |
| Linting | Biome |
---
## What This Demonstrates
This project requires understanding React at the internals level — fiber architecture, commit phases, bitmask flag encoding — combined with the systems engineering of keeping a complex UI performant through Web Workers, custom data structures, and disciplined dynamic imports. The CI CLI demonstrates that performance engineering is not just a development-time activity but a policy enforced across the entire delivery pipeline.