Metrics
What each number in perfbit actually measures, and the caveats worth knowing before you act on one.
At a glance
| Metric | Event type | Reported |
|---|---|---|
| Cold start | cold_start | Once per app launch |
| Warm start | warm_start | On each resume from background |
| Screen load | screen_load | Once per navigation |
| Slow / frozen frames | frame_metrics | Every 10 seconds |
| Crash-free rate | session_start / session_crash | Per session |
| Network timing | network_request | Per request |
Cold start
Measured from the moment the SDK module is first evaluated by the JavaScript engine to the moment the app becomes active. It is reported once per launch.
Note
This is a JavaScript-sidemeasurement. It does not include native process startup or the time spent loading the JS bundle before your imports run, so it will read lower than the "time to first frame" a platform profiler reports. It is designed for comparing your own builds against each other, not for comparison against native tooling.
Because the clock starts at module evaluation, importing the SDK as early as possible makes the number more faithful. An import buried behind heavy module initialisation will under-report.
The dashboard shows cold start as p50 and p95 across the selected window.
Warm start
Time from the app returning to the foreground until it is active again, measured on each background → active transition after the first launch. Reported as p50.
Screen load
For each navigation, the time from the navigation state changing until InteractionManager.runAfterInteractions()fires — React Native's signal that the transition animation and queued interaction work have completed.
- The screen name is the deepest active route name, so a screen nested inside tabs inside a stack reports its own name.
- Re-navigating to the current screen is ignored — only actual screen changes are recorded.
- Requires
navigationRef. See Screen tracking.
The Screens view reports p50, p75 and p95 per screen plus a sample count, and a latency histogram bucketed at 0–200ms, 200–500ms, 500ms–1s, 1s–2s and 2s+.
Slow and frozen frames
A requestAnimationFrame loop measures the gap between successive frames and classifies each one:
| Classification | Frame duration | Meaning |
|---|---|---|
| Slow frame | > 16.67ms | Missed the 60fps budget — visible as minor jank |
| Frozen frame | > 700ms | The UI was unresponsive long enough for a user to notice |
Counts are aggregated over a 10-second window and sent as one event carrying total_frames, slow_frames and frozen_frames. The dashboard turns these into rates: slow_frames / total_frames as a percentage, and the same for frozen frames. A frame is counted as frozen or slow, never both.
Note
perfbit reports frame rates, not an FPS number. A slow frame rate of 4% means 4% of rendered frames exceeded the 60fps budget during the selected window.
Crash-free session rate
A session begins when init()runs and is identified by a random UUID. The SDK wraps React Native's global error handler, so an unhandled JavaScript error marks that session as crashed and triggers an immediate flush. Your app's own error handler still runs afterwards — perfbit chains to it rather than replacing it.
The rate is the share of sessions in the window that were never marked crashed.
Heads up
JavaScript errors only. Native crashes — an iOS/Android signal, a bad native module — kill the process without the JS handler ever running, so they are not counted. There is also no stack-trace browser or crash grouping in the dashboard; crash reporting here is a rate, not a debugger. Pair perfbit with a dedicated crash reporter if you need native crash detail.
Network timing
The SDK wraps global.fetch and XMLHttpRequest.prototype.open/send, recording the wall-clock duration and response status of every request your app makes — including those made by libraries like Axios, which sit on top of these.
URL handling
URLs are reduced to origin + pathname before being recorded. Query strings are stripped, so tokens or IDs in a query never leave the device. Request and response bodies are never read.
Note
Path parameters are not templated — /users/123 and /users/456 are recorded as two distinct endpoints. Apps with IDs in the path will see their Network view fragment across many rows.
request_size_bytes and response_size_bytes exist in the payload schema but are always sent as 0 — payload size is not currently measured.
The Network view groups by method plus URL and reports p50, p95, error rate (responses with status ≥ 400) and request count, sorted by p95 descending. Requests that throw — DNS failure, timeout, offline — are recorded with a duration but no status code.
Percentiles
Percentiles are computed by sorting the raw samples in the window and taking the value at ceil(p × n) - 1. They are calculated live from stored events at query time, so changing the time range or platform filter recomputes them rather than reading a rollup.
Daily trend lines use the median of each day's samples, grouped by the date the event was received.