Performance & network
Automatic Web Vitals and page-load/route transactions, a resource waterfall and jank detection, network request capture, and a manual transaction/span API.
Performance monitoring is on by default and captures real-user metrics per route, with no instrumentation.
Web Vitals#
LCP, INP, CLS, FCP, and TTFB are collected automatically per route, with attribution (what caused the metric), so you can act on a regression rather than just watch a number.
Page-load and route transactions#
The SDK builds a page-load transaction from Navigation Timing, and SPA route transactions on client navigation, including a resource waterfall and jank detection via Long Tasks / LoAF.
Manual transactions and spans#
For the work only you can name, wrap it in a transaction and spans:
const txn = ScaleBun.performance.startTransaction('checkout');const span = txn.startSpan('validate-cart');// … do work …span.finish();txn.finish();For a single timed span without a transaction tree, or a one-off number:
ScaleBun.performance.startTrace('cart-hydrate');// … do work …ScaleBun.performance.stopTrace('cart-hydrate'); // emits the elapsed time
ScaleBun.performance.sendMetric('items_rendered', 42);startTransaction is safe to call before the engine loads — it returns a no-op
handle rather than throwing, so instrumentation never has to be guarded.
Tuning#
await ScaleBun.init({ clientKey, apiBaseUrl, performance: { tracesSampleRate: 1, slowTransactionMs: 3000 }, network: { captureBodies: false, slowRequestMs: 2000, denyUrls: [/\/healthz$/], beforeSend: (item) => item, },});| Option | Default | What it does |
|---|---|---|
performance.tracesSampleRate | 1 | Thins transactions. Slow and errored ones are always kept — this only drops fast, clean ones. |
performance.slowTransactionMs | 3000 | The "slow" threshold that forces a keep. |
network.sampleRate | 1 | Thins successful requests only; failed and slow are always kept. |
network.slowRequestMs | 2000 | Flags and force-keeps slow requests. |
network.trackWebSocket / trackSse | true | Lifecycle and counters, in a lazy chunk. |
network.captureBodies | false | Opt-in body capture, redacted and capped. |
network.bodyMaxBytes | 8192 | Hard cap when bodies are on. |
network.contentTypeAllowlist | ['application/json','text/plain'] | Which types are eligible. |
network.denyUrls | — | Never capture matching URLs. |
network.beforeSend | — | Transform, or return null to drop. |
Because sampling is biased toward keeping what went wrong, dialling these down costs you volume on healthy traffic rather than visibility into problems.
Network monitoring#
fetch, XHR, WebSocket, SSE, and sendBeacon are auto-captured with a
failure taxonomy, timing breakdown, GraphQL awareness, and templated routes (so
/users/42 and /users/99 group as /users/:id). Request and response
bodies are off by default.
The SDK injects a W3C traceparent header on same-origin requests
(tracePropagation, on by default) so front-end spans stitch to your backend
traces. Cross-origin propagation is opt-in and requires the server to CORS-allow
the header.
Next#
Session replay — pair metrics with what the user saw.
Errors & crashes — capture failures with context.