Skip to content
TraceItX Docs
Documentation

Breadcrumbs

The trail that led to the bug: routes, clicks, console, network and errors, on one clock.

Updated

Every report carries a single chronological chain, captured automatically. You only write code when you want to add something of your own.

{
  "t": 1765800000123,
  "seq": 42,
  "kind": "network",
  "level": "error",
  "message": "POST /api/cart",
  "data": {
    "reqId": 17,
    "status": 500
  }
}

Kinds

KindRecorded when
navigationA route change. See Screen tracking.
tapThe user interacts with a control.
consoleYour app logs. See Console & network.
networkA request completes.
lifecycleThe tab is hidden or restored (visibilitychange).
errorAn uncaught error fires. See Crash reporting.
customYou called addBreadcrumb.

Which kinds are recorded at all is configurable per app on the server — see Server config.

How a tap is labelled

This is the part worth understanding, because you control the output.

A pointerdown is captured, then the SDK climbs at most five ancestors looking for the nearest interactive element — button, a, input, select, textarea, summary, label, or anything with role="button" or role="link". From that element it takes the first of:

  1. aria-label
  2. Visible text, whitespace-collapsed, capped at 48 characters
  3. tag#id
  4. The tag name alone

So aria-label is the lever. A button whose only content is an icon lands in the trail as tap button unless you label it, and “the user clicked something” is the crumb that wastes a triager’s time.

There are no component names here. Reports carry DOM-level context — tag, id, role, aria-label — not framework component names. That is the same on @traceitx/react, which shares this capture core.

An element that intersects the sensitive mask set is labelled [masked] and no text is read out of its subtree at all.

Adding your own

traceitx.addBreadcrumb({
  message: 'Coupon applied',
  kind: 'custom',
  level: 'info',
  data: { code: 'SUMMER25', discount: 0.25 },
});

addBreadcrumb never throws. An unknown kind coerces to custom and an invalid level is dropped rather than passed through — instrumentation must not be able to break the app it is instrumenting.

Put identifiers in data, not in message. data survives as structured fields an agent can filter on; message is prose, and prose is what gets truncated first.

The shared clock

t is absolute epoch milliseconds, on the same clock as the session replay — that is what lets a viewer line a crumb up against the frame that was on screen when it fired. seq is a monotonic per-session counter breaking ties inside the same millisecond, so ordering stays stable at high event rates.

Limits

Caps and trimming behaviour — the entry cap, the byte budget, and the trim markers that record what was dropped — are in Errors & limits.