Skip to content
Everframe Docs
Documentation

Screen tracking

Nothing to wire up. Every client-side route change becomes a breadcrumb.

Updated

The SDK patches history.pushState / history.replaceState and listens for popstate, so every route change — and every back/forward — becomes a crumb. That covers React Router, Next.js and TanStack Router with no configuration.

// nothing beyond the normal provider
<EverframeProvider config={{ apiKey: 'evf_live_…' }}>
  <App />
</EverframeProvider>
// → breadcrumb: "/cart → /checkout?step=2"

The screen name is pathname + search. Same-URL transitions are dropped, and the patch installs once per page no matter how many times the provider remounts — React StrictMode and Fast Refresh are safe.

The hash-routing gap

Screen names are built from pathname and search only. A router that navigates purely in the fragment — /#/checkout, React Router’s HashRouter — never changes the tracked URL and emits no crumbs.

Switch that route tree to BrowserRouter, or your router’s equivalent History-API mode, to get the trail automatically. Or keep the hash router and mark the screens yourself — one line per screen:

import { useEverframeScreen } from '@everframe/react';

function CheckoutScreen() {
  useEverframeScreen('Checkout');
  …
}

Marking screens by hand

useEverframeScreen and its declarative twin <EverframeScreen> exist for anything the URL does not describe: a hash router, a wizard whose steps share one route, a modal stack, or a canvas app with no routes at all.

import { useEverframeScreen, EverframeScreen, recordScreen } from '@everframe/react';

// Unmounted when hidden — mount is the appearance.
useEverframeScreen('Checkout');

// Kept mounted (tabs, a stack) — pass focus instead.
useEverframeScreen(name, { focused: isActive });

// Declarative, for class components.
<EverframeScreen name="Checkout" />

// Imperative, outside the tree entirely — a router callback.
recordScreen('Checkout');

focused defaults to true, which is right when your router unmounts hidden screens. In a tab layout every screen stays mounted, so mount-time marking would record all of them once at startup and nothing after — pass focused there.

All four feed the same chain as the automatic History-API crumbs, and derive the same from → to shape the native SDKs emit, so a web timeline and a native one read identically in one dashboard.

How the chain behaves

  • One global chain, chronological. A tab switch reads TabA → TabB.
  • The first screen emits nothing. There is nothing to come from yet. It still becomes the from of the next transition.
  • A → A is suppressed. Refocus, remount or a double-wired integration never produce a crumb.
  • A blank name is not a screen. The chain is left untouched, so the next real screen still reports a transition from the last real one.
  • from and to are authoritative. Your own data keys of those names cannot forge the transition they are attached to.

Disabling the navigation breadcrumb kind stops these crumbs and the automatic ones together — the chain keeps tracking underneath, so re-enabling mid-session resumes from the true previous screen rather than a stale one.

Naming screens

Names travel in the report and are shown in triage. They do pass through the breadcrumb buffer’s redaction, so an obvious email or card number in a URL is caught — but redaction recognises shapes, not meaning, and an order id or an internal username sails through. Do not rely on it.

On the web the name comes from the URL, which means the rule is about your routes: prefer /orders/:id shapes over paths that embed an email or a token in the query string. If a route genuinely carries something sensitive, mark it with redaction rules so it is scrubbed before capture.

What lands in the envelope

Navigation crumbs are entries in payload.breadcrumbs, the same timeline that carries taps, console, network, lifecycle and error entries:

{
  "t": 1755500000000,
  "seq": 42,
  "kind": "navigation",
  "message": "/cart → /checkout",
  "data": {
    "from": "/cart",
    "to": "/checkout"
  }
}

t is the shared epoch-millisecond clock that also stamps the session replay, so a receiver can align a crumb to a replay frame by direct comparison.

Caps and trimming behaviour are in Errors & limits.

Troubleshooting

SymptomLikely cause
No crumbs on an app that clearly changes routesHash-based routing, or a router that re-renders without touching the History API.
Crumbs stop the moment the reporter opensExpected. The trail is frozen when the report opens so the reporter’s own UI never pollutes the evidence.
The same screen appears twice in a rowIt should not — A → A is suppressed. If you see it, the two URLs differ in search.