react-render-profile-mcp
Decodes React DevTools Profiler exports and detects spurious renders
react-render-profile-mcp 🐸⚡
An MCP server that decodes React DevTools Profiler exports into actionable render summaries for AI agents.
Your agent just refactored a context provider. Now 80 components re-render on every keystroke. It has no idea.
🤔 The Problem
AI agents modify React state management, restructure component trees, and refactor context — and they're completely blind to the performance impact.
The React DevTools Profiler can capture exactly what happened: which components re-rendered, why, and how long it took. But the exported .json is a dense structure with Fiber IDs, encoded operations, and microsecond timing data across hundreds of commits.
{
"version": 5,
"dataForRoots": [{
"commitData": [{
"fiberActualDurations": [[3, 15.2], [4, 8.1], [142, 3.2], ...],
"fiberSelfDurations": [[3, 3.9], [4, 4.9], [142, 3.2], ...],
"changeDescriptions": { "4": { "props": [], "didHooksChange": false, ... } },
...
}],
...
}]
}
The agent can't parse this. Even if it could, it can't run the aggregation to identify which components are wasting renders — it's raw tick data, not a performance summary.
✅ The Fix
This MCP server loads the profiler export and gives agents exactly what they need:
- Which components re-rendered the most — and how much CPU time they consumed
- Which re-renders were spurious (props reference changed, no actual keys differed) vs context-driven (React.memo can't help)
- Whether a component is being destroyed and recreated instead of updated (unstable
keyprop) - What triggered a specific render cascade — and whether it was a React 18 transition commit
- Which components are
React.memocandidates — and which are too fast for memo to help
🛠️ Tools
get_render_summary
High-level overview: total commits, total render time, top 5 slowest components, total spurious render count. Each component includes lifecycle counts so the agent can spot key-instability patterns.
{
"total_commits": 24,
"total_render_ms": 312.4,
"total_spurious_renders": 18,
"top_components": [
{
"component": "ProductList",
"render_count": 24,
"mount_count": 1,
"unmount_count": 0,
"update_count": 23,
"lifecycle_anomaly": false,
"total_self_ms": 89.2,
"pct_of_total": 28.55
},
{
"component": "ListItem",
"render_count": 20,
"mount_count": 20,
"unmount_count": 19,
"update_count": 0,
"lifecycle_anomaly": true,
"total_self_ms": 61.1,
"pct_of_total": 19.56
}
]
}
lifecycle_anomaly: true means the component is being destroyed and recreated on every render instead of updating — the classic unstable key prop bug. React rebuilds the entire DOM subtree each time.
Use first to understand the scale of the problem.
find_spurious_renders
Components that re-rendered unnecessarily, with the root cause classified so the agent knows the correct fix.
{
"spurious_renders": [
{
"component": "ProductList",
"render_count": 24,
"spurious_count": 23,
"wasted_ms": 84.3,
"render_trigger": "UNSTABLE_PARENT_REF",
"concurrent_yield": false,
"recommendation": "Wrap with React.memo — re-renders are driven by unstable object/function/array references from the parent."
},
{
"component": "UserAvatar",
"render_count": 12,
"spurious_count": 11,
"wasted_ms": 18.7,
"render_trigger": "CONTEXT_UPDATE",
"concurrent_yield": false,
"recommendation": "React.memo cannot help here — context updates bypass memo. Stabilize the context value with useMemo, or split the context."
},
{
"component": "DeferredList",
"render_count": 8,
"spurious_count": 6,
"wasted_ms": 24.1,
"render_trigger": "UNSTABLE_PARENT_REF",
"concurrent_yield": true,
"recommendation": "INTENTIONAL_CONCURRENT_YIELD — all spurious renders happened during startTransition/useDeferredValue commits. This is expected React 18 behavior; do not add React.memo."
}
]
}
UNSTABLE_PARENT_REF— fix withReact.memoCONTEXT_UPDATE— fix by stabilizing the context value;React.memodoes nothing hereconcurrent_yield: true— React 18 intentionally renders these multiple times during transitions; do not optimize
get_hottest_components
Top N components by self CPU time (excluding children). Includes transition_render_count so the agent can see what fraction of renders are React 18 deferred work.
{
"total_profile_ms": 312.4,
"components": [
{
"component": "ProductList",
"render_count": 24,
"transition_render_count": 3,
"total_self_ms": 89.2,
"avg_self_ms": 3.72,
"pct_of_total": 28.55
}
]
}
trace_render_cascade
For a specific commit, shows what triggered it and every component that re-rendered as a result, sorted by duration. Now includes is_concurrent_commit so the agent knows whether this is a React 18 transition render.
{
"commit_index": 7,
"is_concurrent_commit": true,
"trigger": "SearchInput",
"total_commit_ms": 28.4,
"cascade": [
{
"component": "ProductList",
"self_ms": 18.2,
"actual_ms": 18.2,
"reason": "parent re-rendered (unstable props reference)"
},
{
"component": "SearchInput",
"self_ms": 9.1,
"actual_ms": 9.1,
"reason": "hook changed"
}
]
}
is_concurrent_commit: true means this commit was triggered by startTransition or useDeferredValue. React intentionally re-renders and discards incomplete trees in these lanes — flagging them as regressions would be wrong.
Use to understand propagation — why did 40 components re-render from one click?
suggest_memoization
Memoization suggestions with viability scores. Not every component with spurious renders benefits from React.memo — for components that render in under 2ms, the Object.is() comparison overhead can exceed the render cost.
{
"suggestions": [
{
"component": "ProductList",
"render_count": 24,
"spurious_count": 23,
"wasted_ms": 84.3,
"avg_render_ms": 3.72,
"prop_stability": "UNSTABLE_REFERENCES",
"recommendation": "MEMOIZE",
"reasoning": "Re-rendered 23×/24 times with unchanged props, wasting 84.3ms. Wrap with React.memo to skip renders when props are shallowly equal."
},
{
"component": "Badge",
"render_count": 30,
"spurious_count": 28,
"wasted_ms": 4.2,
"avg_render_ms": 0.15,
"prop_stability": "UNSTABLE_REFERENCES",
"recommendation": "DO_NOT_MEMOIZE",
"reasoning": "avg render time (0.15ms) is below 2ms — React.memo comparison overhead likely exceeds render cost. Fix the unstable reference in the parent instead."
},
{
"component": "DeferredList",
"render_count": 8,
"spurious_count": 6,
"wasted_ms": 24.1,
"avg_render_ms": 3.01,
"prop_stability": "UNSTABLE_REFERENCES",
"recommendation": "INTENTIONAL_CONCURRENT_YIELD",
"reasoning": "All spurious renders occurred during startTransition/useDeferredValue commits. React 18 intentionally renders these components multiple times while resolving deferred work — do not add React.memo."
}
]
}
MEMOIZE— high ROI, wrap withReact.memoDO_NOT_MEMOIZE— too fast; memo overhead exceeds render cost; fix the parent reference insteadINTENTIONAL_CONCURRENT_YIELD— React 18 concurrent behavior; do not optimize
🚀 Setup
Claude Desktop
{
"mcpServers": {
"react-render-profile": {
"command": "npx",
"args": ["-y", "react-render-profile-mcp"]
}
}
}
Cursor / VS Code / Any MCP client
{
"react-render-profile": {
"command": "npx",
"args": ["-y", "react-render-profile-mcp"]
}
}
📋 How to Export a Profile
- Open React DevTools in Chrome DevTools
- Go to the Profiler tab
- Click Record, interact with your app, click Stop
- Click the Save icon (💾) to export the
.jsonfile - Pass the absolute path to any tool as
profile_path
🔧 How It Works
The parser decodes the React DevTools Profiler export format (version 5):
- fiberID → component name from
snapshots(primary) or the encodedoperationsinteger array (fallback) - Spurious render detection via
changeDescriptions.props === []— React records an empty array when the props object reference changed but no individual prop keys differed - Context render detection via
changeDescriptions.context === true— surfaced separately because React.memo cannot prevent these - Lifecycle tracking via
isFirstMountper commit andTREE_OPERATION_REMOVEopcodes in the operations array - Concurrent render detection via
commit.priorityLevel—"Low Priority"and"Idle"indicatestartTransition/useDeferredValuelanes - Aggregation across all commits: total self time, render counts, wasted time per component
No React dependency. No DevTools packages. Pure JSON parsing.
📖 Agent Workflow
1. get_render_summary → understand the scale; spot lifecycle_anomaly (key instability)
2. find_spurious_renders → classify root cause: UNSTABLE_PARENT_REF vs CONTEXT_UPDATE vs concurrent_yield
3. trace_render_cascade → understand propagation; check is_concurrent_commit before flagging as regression
4. suggest_memoization → get verdicts: MEMOIZE / DO_NOT_MEMOIZE / INTENTIONAL_CONCURRENT_YIELD
🐸 Part of the MCP Toolbelt
Built alongside:
- tailwind-context-resolver-mcp — resolve Tailwind design tokens and validate class strings
- v8-cpu-profile-decoder-mcp — V8 CPU profile analysis for Node.js performance
License
MIT
İlgili Sunucular
Alpha Vantage MCP Server
sponsorAccess financial market data: realtime & historical stock, ETF, options, forex, crypto, commodities, fundamentals, technical indicators, & more
Chart
A Model Context Protocol server for generating visual charts using AntV.
n8n-MCP
Provides AI assistants with access to n8n node documentation, properties, and operations.
GodotIQ
The intelligent MCP server for AI-assisted Godot 4 development. 35 tools for spatial intelligence, code understanding, flow tracing, and visual debugging. 22 free, full suite $19.
Scalekit MCP server
Give your agents access to 100+ connectors and 500+ tools with secure, per-user managed authentication for every tool call
Rails MCP Server
An MCP server for Rails projects, allowing LLMs to interact with your application.
SpecBridge
Automatically generates MCP tools from OpenAPI specifications by scanning a folder for spec files. No configuration is needed and it supports authentication via environment variables.
GraphQL Schema
Exposes GraphQL schema information to LLMs, allowing them to explore and understand the schema using specialized tools.
NSAF MCP Server
An MCP server for the Neuro-Symbolic Autonomy Framework (NSAF), enabling AI assistants to interact with the framework.
Claude Memory MCP Server
A persistent memory server for Large Language Models, designed to integrate with the Claude desktop application. It supports tiered memory, semantic search, and automatic memory management.
Android MCP Server
Control Android devices via the Android Debug Bridge (ADB).