ScaleBun
Skip to article

Performance

react-nativeDeveloper

App launch, screen load, network timing and frame metrics — the tiered collector model, custom traces, and how to control overhead.

Updated Reviewed

Performance monitoring is tiered by cost, not on/off. Cheap event-driven collectors run by default; expensive continuous ones are sampled.

The two tiers#

TierCollectorsDefaultCost
Tier 1App launch, screen load, network, UI-hang watchdogAll onEvent-driven — fires only when something happens
Tier 2Frame metrics, JS stallOn, sampled at 0.1Continuous — measures every frame

Tier 1 is effectively free: each collector emits once per launch, per navigation, per request, or only when a real hang occurs. Tier 2 runs continuously while active, which is why it is sampled per session rather than per event — a session either participates or it does not, so a sampled trace is internally consistent.

TypeScript
await ScaleBun.init({  projectId: '…',  publishableKey: '…',  apiBaseUrl: '…',  performance: {    enabled: true,    tier1: { appLaunch: true, screenLoad: true, network: true, uiHang: true },    tier2: { enabled: true, sampleRate: 0.1 },  },});

Custom traces#

Measure a span your app cares about — a checkout, a sync, an image pipeline:

TypeScript
const traceId = ScaleBun.performance.startTrace('checkout');
ScaleBun.performance.addTraceSpan(traceId, 'validate_cart', 42);ScaleBun.performance.addTraceMeasurement(traceId, 'cart_items', 3, 'count');
ScaleBun.performance.stopTrace(traceId);

startTrace returns null when performance monitoring is inactive — not entitled, disabled, or before init() resolved. Guard on it rather than assuming a string:

TypeScriptRecommended
const traceId = ScaleBun.performance.startTrace('checkout');if (traceId) {  // …  ScaleBun.performance.stopTrace(traceId);}
TypeScriptAvoid
// Throws nothing, but silently attaches spans to a trace that does not exist.const traceId = ScaleBun.performance.startTrace('checkout')!;ScaleBun.performance.addTraceSpan(traceId, 'step', 10);

Always stop a trace you start. An unstopped trace never reports, so a missing stopTrace on an error path shows up as missing data rather than as a slow trace — put it in a finally.

Screen load#

If you set the navigation ref, screen-load timing is automatic. Mark it manually when a screen's useful content arrives later than its mount — a list that renders a skeleton first, for example:

TypeScript
ScaleBun.performance.markScreenLoadStart('ProductList');await loadProducts();ScaleBun.performance.markScreenLoadEnd('ProductList');

This is the difference between measuring "the screen appeared" and "the screen became useful", and the second is what your users experience.

Checking state#

TypeScript
ScaleBun.performance.isActive();  // false when disabled or not entitled

Native module requirement#

The UI-hang watchdog and Tier-2 collectors need the native module, so they are unavailable in Expo Go. Use a development build. Tier-1 network and screen-load timing work without it.