This tool works with both Expo and React Native CLI projects. Just install and go.
Every React Native developer has watched JS FPS drop and asked the same question: what is eating the thread? JS Top answers it live, like Task Manager or Activity Monitor answers it for your computer — a ranked table of task origins (setInterval ← startPolling, Promise.then ← api.ts, requestAnimationFrame ← rafSpinLoop) with time consumed, call counts, and share of thread busy time, updating as you watch.
It's pure JavaScript — no native module, no dev client, no debugger attached. It works in Expo Go and even in release builds.
npm install @buoy-gg/js-topWith auto-discovery, installing the package is all you need — the JS TOP tool and its toggleable busy-pill HUD appear in your floating menu automatically. Or register it explicitly:
import { FloatingDevTools } from "@buoy-gg/core";
import { jsTopPreset, jsTopModalPreset } from "@buoy-gg/js-top";
<FloatingDevTools apps={[jsTopModalPreset, jsTopPreset]} />
import { FloatingDevTools } from "@buoy-gg/core";
import { jsTopPreset, jsTopModalPreset } from "@buoy-gg/js-top";
<FloatingDevTools apps={[jsTopModalPreset, jsTopPreset]} />
You can't sample a blocked JS thread from JavaScript — so JS Top doesn't sample. Instead it wraps every entry point work can take onto the thread (setTimeout, setInterval, setImmediate, requestAnimationFrame, queueMicrotask, Promise reactions, and legacy-bridge call-ins) and measures each callback precisely, attributing the time to where the callback was scheduled from.
Alongside that, a calibrated high-frequency probe measures true thread occupancy from timer-gap inflation, and React Native's built-in longtask observer flags every 50ms+ block. Anything the wrappers can't see shows up honestly as an unattributed row — the tool never pretends to a coverage it doesn't have.
With the MCP server, an agent can call get_js_thread_top — the device samples for a few seconds and returns the ranked table, so "why is JS FPS low?" becomes a one-tool-call answer.