Skip to content

Overview ​

A breadcrumb is a small, timestamped record of something that happened before an error — a console log, a click, a network request, a navigation. They're kept in a rolling in-memory trail on the client and attached to the next captured exception, giving you the sequence of events that led up to a failure instead of just the failure itself.

Breadcrumbs only travel with exceptions. Logs, traces, and spans don't carry a breadcrumb trail — there's nothing "leading up to" a log call the same way there is for a crash.

Adding breadcrumbs manually ​

ts
Buglapse.addBreadcrumb({
    type: 'checkout',
    level: 'info',
    message: 'Applied promo code SUMMER10',
    data: { code: 'SUMMER10', discount: 0.1 },
})

try {
    await chargeCard(order)
} catch (error) {
    Buglapse.captureException(error) // ships with the breadcrumb above already in its trail
}

Only message is required:

  • type — a free-form string used to pick an icon/renderer on the dashboard (see Viewing breadcrumbs on the dashboard). Defaults to "debug" if omitted.
  • level — log | info | warn | error | debug | success. Defaults to "debug". Drives the color of the breadcrumb's marker, independent of type.
  • data — an optional object of structured detail (a payload, a response body, form values). Shown collapsed under the breadcrumb on the dashboard, expandable as raw JSON.
  • timestamp — stamped automatically (new Date().toISOString()) when you call addBreadcrumb(); there's no reason to set it yourself.

Breadcrumbs are global to the client, not scoped to a single captureException() call — anything added anywhere in your app keeps accumulating in the same trail until the next exception ships it.

Automatic breadcrumbs ​

Rather than instrumenting addBreadcrumb() calls by hand throughout your app, BreadcrumbsIntegration records the same kind of trail automatically: console calls, DOM clicks/keypresses, fetch/XHR requests, and SPA navigations (history.pushState/popstate) each show up as a breadcrumb with no code changes needed. It's opt-in — enable it via integrations: [breadcrumbsIntegration()] in init(), or toggle it on from the project's Settings → Loader Script tab if you're on the CDN embed. Each source (console/DOM/fetch/ XHR/history) can be turned off independently; see BreadcrumbsIntegration for the config shape.

Manual addBreadcrumb() calls and the integration's automatic ones land in the same trail and interleave by time — you don't need to choose one or the other.

Trail limit ​

The trail keeps at most the 50 most recent breadcrumbs (MAX_BREADCRUMBS); once it's full, the oldest breadcrumb is dropped as a new one is added. An exception captured after a long-running session only ships the last 50 breadcrumbs leading up to it, not the full session history.

Viewing breadcrumbs on the dashboard ​

An exception's (or an issue's) detail page renders its breadcrumb trail chronologically above the stack trace. Every row's marker is colored by level (error red, warn amber, success emerald, info blue, log/debug neutral) regardless of type — type only picks which component renders the row and how it reads data. Matching is exact on type first, then falls back to the prefix before the first ./- (so a custom "cart.added" type falls through to the prefix cart, which itself has no dedicated component and lands on the generic row below).

  • console — title is "Console {level}" (e.g. "Console warn"). The subtitle reads data.args: an array of the raw arguments passed to the console call, joined with spaces (non-strings JSON.stringify'd individually). If data.args isn't an array, it falls back to message. No expandable data section.
  • interaction (and the legacy ui.click/ui.keypress type) — the title is humanized from data.kind ("click" → "Click", "keypress" → "Keyboard", anything else just capitalized); with no data.kind, it's derived from a ui.* type instead, or "Interaction" if neither is present. The subtitle is data.target — the ancestor-path element description (button.icon-button > svg "Submit"-style string) — falling back to message. No expandable data section.
  • navigation — title is fixed to "Navigation". The subtitle reads data.from/data.to: "{from} → {to}" when both are present, just to when there's no from, or message if neither field exists.
  • fetch / xhr / http — title is "Fetch"/"XHR"/"HTTP" to match the type (default "HTTP" for anything else routed here). The response status (data.status) is shown next to the title, colored red when level is "error" and emerald otherwise. The subtitle joins data.method + data.url, falling back to message if neither is set. Any other keys left in data beyond method/url/status are shown in a collapsed "Data" section as raw JSON — the section only appears if there's something left over.
  • Anything else falls back to a generic row: the title is the type capitalized, and the subtitle is always message. Its icon is picked by an exact match on type against a small fixed set (log, exception, error, nav, request, form, checkout, cart, query, search each get their own icon; anything unrecognized gets a generic info icon) — this lookup is exact, not prefix-based, so "cart.added" still gets the generic icon even though "cart" would match. If the breadcrumb carries data (and it's not an empty array), it's shown as a collapsed "Data" section — expand it to see the raw JSON rather than a flattened key/value list, since breadcrumb data can be arbitrarily shaped.