DEV Community

Cover image for Your AI Agent Just Broke Your React Performance. It Has No Idea
Albert Alov
Albert Alov

Posted on

Your AI Agent Just Broke Your React Performance. It Has No Idea

React DevTools Profiler gives you all the data you need to fix re-renders. Your AI agent can't read it. Here's how to fix that with an MCP server.

You open React DevTools Profiler, record a session, and export the .json file. It's got everything: which components re-rendered, how long each one took, which props changed, which hooks fired.

Then you paste it into Claude or Cursor and ask: "What's causing the performance problem?"

The agent stares at raw JSON — thousands of lines of fiber IDs, opcode arrays, and microsecond timestamps — and does its best. But it's reading tea leaves. It doesn't know what a "spurious render" is in this format. It can't decode the operations integer array. It doesn't know that props: [] means a component re-rendered even though no props actually changed.

That's the gap react-render-profile-mcp fills. 🔍


🤔 What the profiler actually exports

React DevTools serializes profiler data in a specific binary-ish format that most developers have never had to parse manually:

{
  "version": 5,
  "dataForRoots": [{
    "commitData": [{
      "changeDescriptions": [[3, {"isFirstMount": true, "props": null}], [4, {...}]],
      "fiberActualDurations": [[3, 15.2], [4, 8.1]],
      "fiberSelfDurations": [[3, 3.9], [4, 4.9]],
      "duration": 15.2,
      "timestamp": 100.0
    }],
    "snapshots": [[3, {"displayName": "App", "children": [4, 5]}]],
    "operations": [1, 0, 3, 2, 65, 112, 112, ...]
  }]
}
Enter fullscreen mode Exit fullscreen mode

A few non-obvious things buried in there:

⚠️ changeDescriptions is an array of pairs, not an object. It's serialized as Map.entries()[[fiberID, desc], ...]. If you parse it naively as JSON object keys, you'll get garbage.

⚠️ props: [] means spurious render. An empty array means the component re-rendered but zero prop keys actually changed — the reference was unstable. props: null means unknown. props: ["value"] means the value prop genuinely changed.

⚠️ operations is an opcode array. It encodes tree mutations (mount, unmount, reorder) with a string table at the start. You need to decode it to map fiber IDs to component names when snapshots aren't present.

⚠️ fiberSelfDurationfiberActualDuration. Self = time in this component only. Actual = self + children. For finding hotspots, you want self time.

The MCP server handles all of this so the agent doesn't have to.


🛠️ The 5 tools

get_render_summary

High-level overview of the entire recording:

Total commits: 12
Total render time: 847.3ms
Spurious renders: 8
Top components by self time:
  ProductList — 312.4ms (6 renders)
  SearchInput — 89.1ms (12 renders)
  Sidebar — 44.2ms (3 renders)
Enter fullscreen mode Exit fullscreen mode

find_spurious_renders

Detects components that re-rendered but had no actual prop or state changes:

Spurious renders found:
  ProductList — 6 spurious renders, 312.4ms wasted
    → props ref changed but no keys differed
Enter fullscreen mode Exit fullscreen mode

This is the classic "missing React.memo" pattern. The component re-renders every time a parent renders, even though nothing it depends on changed.

get_hottest_components

Components ranked by total self time across all commits. Useful for finding where to optimize first.

trace_render_cascade

Given a commit index, shows what triggered it and what else re-rendered as a result:

Commit 3 — triggered by: SearchInput (hook changed)
Cascade:
  ProductList — re-rendered (unstable props)
  Sidebar — re-rendered (context changed)
Enter fullscreen mode Exit fullscreen mode

This is how you find the root cause instead of just the symptom.

suggest_memoization

Combines spurious render detection with self-time data to generate specific recommendations:

Suggestions:
  ProductList → React.memo
    Reason: 6 spurious renders, 312.4ms wasted
    Self time: 52.1ms avg per render
Enter fullscreen mode Exit fullscreen mode

⚡ Setup

{
  "mcpServers": {
    "react-render-profile": {
      "command": "npx",
      "args": ["-y", "react-render-profile-mcp"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Then record a session in React DevTools → Profiler → export as JSON → give the file path to your agent.


🚀 The workflow

Instead of pasting raw JSON and hoping:

"Load the profile at /tmp/myapp.profile.json and find spurious renders"

The agent calls find_spurious_renders with the file path, gets back structured data with component names, counts, and wasted milliseconds — and gives you a concrete list of what to fix.

"Load the profile and trace what caused the slow commit at index 3"

trace_render_cascade returns the trigger component, the cascade, and the reason for each re-render.

The agent goes from "I see some fiber IDs with large durations" to "ProductList is re-rendering 6 times spuriously, wrapping it in React.memo would save 312ms."


📦 Links

npx react-render-profile-mcp
Enter fullscreen mode Exit fullscreen mode

Top comments (0)