Axonpack
@axonpack/expo-devtoolsReference

Performance tab

Statistics cards, user timing, interactions and long tasks.

The toolbar carries the record button, the clear button, then the section chips:

[⏺] [⊘] │ (Statistics) (User timing) (Interactions) (Long tasks)

Only the chosen section is mounted, which is deliberate: the charts stop re-rendering while you read a list, and a burst of long tasks does not re-render a chart. The three list chips carry a live count when they have entries.

This tab starts paused (performance.disabledByDefault defaults to true) because measuring costs something. While paused the collectors are detached rather than left running and filtered, so a paused tab costs nothing at all; pressing record attaches them fresh and re-reads whatever the platform still has buffered. Every empty list shows the same Not recording panel with a Start recording button.

performance.historySize (default 120) caps the memory samples, long tasks, user timing entries and interactions kept.

Statistics

Four cards plus the startup breakdown.

Frames per second

FieldMeaning
JS threadCurrent frames per second from a requestAnimationFrame delta loop, sampled every 500 ms.
Main threadCurrent frames per second from a native display-link counter. Reads dev build without the native module.
ChartBoth threads on one plot, one colour per thread, 60 buckets of 5 s, covering the last 5 min. Each bucket keeps its worst reading.
AxisScaled to the confirmed peak (min 60) plus headroom, so a 120 Hz device is not clipped.

The gap between the two lines is the reading that matters: a healthy JS line above a collapsed main line is an app that feels frozen while every JS metric says it is fine.

Interactions card

The slowest event-to-next-paint seen, with the average and the count as the hint. Before anything is captured it reads Slowest event to next paint.

Memory

FieldMeaning
JS Heap plotperformance.memory used heap, captioned of <total> allocated. Reads This JS engine doesn't report it on JSC/V8.
App memory plotWhole-process footprint, what the OS holds against you. Needs a dev build.
Device memory meterUsed against total RAM, captioned with what is still available to this app. On Android that is system-wide free memory; on iOS it is what the process can still claim. Needs a dev build.

Both plots are sampled on performance.sampleIntervalMs (default 1 s) and span historySize samples, two minutes at the defaults. Each carries its own peak marker.

Storage

Disk space, not the Storage tab's contents — a Used meter against the data partition's total, captioned with the free space. Android only, and needs a dev build; the card says which of the two is missing.

iOS is absent on purpose: systemFreeSize is one of Apple's required-reason APIs, and a library reading it would push a privacy-manifest declaration onto every app that embeds it.

Startup

Process start to first render, read once at launch. Up to two blocks:

BlockRows
Measured by this packageTotal · Native startup · Bundle eval · App setup · To first render
Reported by the platformTotal · Native init · Runtime setup · Bundle eval

The measured block comes from the native module's real process start time, so it works where the platform's own markers are all null. Its phase boundaries are this package's own load points, so they shift a little with your import order; the earlier you call .init(), the truer App setup is. The platform block is performance.rnStartupTiming, and a dash means the platform never reported that marker. The whole section is hidden when neither is available.

User timing

Marks and measures you record yourself, newest first.

ColumnMeaning
NameThe name you passed.
KindAn icon, not a word: a pin for a mark, a ruler for a measure.
DetailYour detail, stringified, when you passed one.
TimeWall-clock time it was recorded.
DurationThe measured span; a mark shows .

This is the only list here that can point at a specific piece of your code, which makes it the answer to a long task you cannot explain. See mark and measure.

Interactions

Anything slower than performance.interactionThresholdMs (default 100 ms) from event to next paint.

ColumnMeaning
NameThe event type.
handler XmsHow long your handler itself held the JS thread.
DurationEvent to next paint, colour-coded by severity.

A small handler under a large total means the interaction was stuck behind something else rather than being slow itself. The platform rounds durations to 8 ms and never reports anything under 16 ms.

Long tasks

Stretches of JavaScript that ran without yielding for longer than performance.longTaskThresholdMs (default 150 ms), newest first: name, wall-clock time, and duration colour-coded by severity. At 60 fps a frame is 16.7 ms, so 150 ms is about nine frames lost; past roughly 200 ms it reads as a freeze.

A long task tells you when to look, never at what: React Native's PerformanceLongTaskTiming returns a permanently empty attribution array. Correlate against what the app was doing, or wrap the suspect in mark and measure.

Both this list and Interactions show a note when the platform's own buffer overflowed and discarded entries before the panel could read them.

On this page