Skip to main content

Breadcrumbs and Session Replay: Debug User Issues

A stack trace tells you what failed, but not why. Breadcrumbs and session replay tell you what the user did before the crash. Breadcrumbs are a timestamped log of user actions (clicks, navigation, fetch requests, console messages) before an error. Session replay is a video-like recording of the DOM state and user interactions. Together, they transform debugging from "I see an error in the logs" to "I watched the user's screen and I see exactly when they clicked the button and what happened."

Understanding Breadcrumbs

A breadcrumb is a single timestamped event in a user's session. Sentry captures breadcrumbs automatically for many event types:

  • User interactions: clicks, form inputs, keyboard events.
  • Navigation: page views, route changes.
  • HTTP requests: fetch calls, API responses, timeouts.
  • Console output: console.log, console.warn, console.error.
  • JavaScript errors: earlier errors that did not crash the app.

When an error occurs, Sentry attaches the last 100 breadcrumbs to the event. Here is an example breadcrumb timeline for a checkout error:

2026-06-02 14:22:01 - User clicked "Add to Cart"
2026-06-02 14:22:05 - Navigation: /cart
2026-06-02 14:22:08 - HTTP POST /api/checkout (status 200)
2026-06-02 14:22:12 - User filled email field
2026-06-02 14:22:15 - User clicked "Place Order"
2026-06-02 14:22:16 - HTTP POST /api/orders (status 500)
2026-06-02 14:22:17 - TypeError: Cannot read property orderId of undefined

The breadcrumbs show that the user got a 500 error from the orders endpoint, which likely returned invalid JSON, causing the parse error. Without breadcrumbs, you would only see the TypeError and no idea that an API error triggered it.

Configuring Breadcrumbs in Sentry

Breadcrumbs are enabled by default in Sentry. You can customize which types are captured in your Sentry config:

import * as Sentry from "@sentry/react";

Sentry.init({
dsn: process.env.REACT_APP_SENTRY_DSN,
integrations: [
new Sentry.Breadcrumbs({
console: true, // Capture console.log, .warn, .error
dom: true, // Capture DOM clicks
fetch: true, // Capture fetch requests
history: true, // Capture navigation
xhr: true, // Capture XMLHttpRequest
sentry: true, // Capture Sentry errors
}),
],
});

To disable a specific breadcrumb type, set it to false. For example, if you want to hide console output:

new Sentry.Breadcrumbs({
console: false,
dom: true,
fetch: true,
history: true,
})

Adding Custom Breadcrumbs

In addition to automatic breadcrumbs, you can log custom events:

import * as Sentry from "@sentry/react";

function CheckoutForm() {
const handleSubmit = async (e) => {
e.preventDefault();

// Log a custom breadcrumb
Sentry.captureMessage("User started checkout", "info");

try {
Sentry.addBreadcrumb({
category: "checkout",
message: "Validating form inputs",
level: "info",
timestamp: Date.now() / 1000, // Sentry expects seconds
});

const response = await fetch("/api/checkout", { method: "POST" });

if (!response.ok) {
Sentry.addBreadcrumb({
category: "api",
message: `API error: HTTP ${response.status}`,
level: "warning",
data: { endpoint: "/api/checkout", status: response.status },
});
}
} catch (error) {
Sentry.captureException(error);
}
};

return form onSubmit={handleSubmit}>
{/* form fields */}
/form>;
}

Custom breadcrumbs are useful for tracking high-level user flows (onboarding, payment, feature adoption) so you understand what the user was doing when they encountered an issue.

Session Replay: Recording User Sessions

Session replay records the DOM state, user interactions, and console output during a user's session. When an error occurs, Sentry attaches the replay so you can watch the user's actions like a video. This is invaluable for reproducing complex bugs that depend on specific interaction sequences.

Enable replay in your Sentry config:

Sentry.init({
dsn: process.env.REACT_APP_SENTRY_DSN,
integrations: [
new Sentry.Replay({
maskAllText: true, // Mask sensitive text
blockAllMedia: true, // Do not record media
maskAllInputs: true, // Mask form inputs
}),
],
replaysSessionSampleRate: 0.1, // Record 10% of sessions
replaysOnErrorSampleRate: 1.0, // Record 100% of sessions that error
});

Configuration options:

  • maskAllText: Replace all text with asterisks to protect PII. Set to false only for internal apps.
  • blockAllMedia: Prevent recording of images and videos (privacy and performance).
  • maskAllInputs: Hide form input values.
  • replaysSessionSampleRate: Percentage of normal sessions to record (0.1 = 10%). High values increase storage costs.
  • replaysOnErrorSampleRate: Percentage of sessions that error to record. Set to 1.0 to always record sessions with errors.

With these settings, 10% of all sessions are recorded, but if a user encounters an error, their session is always recorded (even if it was not in the initial 10%). This balances cost and coverage.

Viewing Session Replays in Sentry

When you open an error in Sentry, click the Replay tab to watch a video-like timeline:

0:00 - Page loads
0:05 - User clicks "Products" link
0:08 - Navigation to /products
0:12 - User scrolls down
0:18 - User clicks "Add to Cart" button
0:22 - API call to /api/cart (200 OK)
0:25 - Dropdown appears with options
0:30 - User selects "Blue" color
0:35 - User clicks "Confirm"
0:37 - TypeError: Cannot read colors of undefined

You can scrub through the timeline, see what was on the user's screen at each moment, and understand the exact interaction sequence that triggered the error. This often reveals obvious bugs that would take hours to reproduce otherwise.

Masking Sensitive Data in Replays

By default, Sentry does not record any text or form values. When maskAllText is true, all DOM text becomes ****. This protects PII but makes it hard to debug. To unmask specific safe text, use the unmask option:

new Sentry.Replay({
maskAllText: true,
unmask: [
".product-name", // Allow product names
".error-message", // Allow error messages
],
})

Or, mask only specific sensitive fields:

new Sentry.Replay({
maskAllText: false,
mask: [
'[name="password"]', // Mask password fields
'[name="ssn"]', // Mask SSN fields
'.credit-card-number', // Mask credit card
],
})

Choose the approach that balances debugging needs with privacy. For e-commerce sites, masking all text is safer. For internal tools, selective masking might be sufficient.

Consider a bug where a user's cart becomes corrupted after rapidly clicking "Add to Cart" multiple times. The error occurs randomly and is hard to reproduce. With breadcrumbs and replays, you see:

Breadcrumbs:

14:22:05 - User clicked "Add to Cart"
14:22:06 - HTTP POST /api/cart (200 OK)
14:22:06 - User clicked "Add to Cart" (second click)
14:22:07 - HTTP POST /api/cart (status 409 Conflict)
14:22:08 - User clicked "Add to Cart" (third click)
14:22:09 - HTTP POST /api/cart (status 409 Conflict)
14:22:10 - TypeError: Cannot map items of undefined

Session Replay: Shows the user clicking rapidly, the cart API returning a 409 conflict, and the state becoming corrupted because your error handler for the conflict response was incomplete.

You immediately identify the race condition and add debouncing or request queueing to fix it.

Key Takeaways

  • Breadcrumbs are timestamped logs of user actions before an error; they add crucial context to stack traces.
  • Session replay records the DOM and user interactions as a video timeline, letting you watch errors happen.
  • Enable replay in production with replaysSessionSampleRate: 0.1 and replaysOnErrorSampleRate: 1.0 to balance cost and visibility.
  • Mask sensitive text and form values to protect PII while maintaining debuggability.
  • Custom breadcrumbs let you log high-level user flows (checkout, onboarding, feature adoption).

Frequently Asked Questions

Does session replay increase my Sentry costs?

Yes, but replay is priced separately and includes a free quota. A typical replay is 50–200 KB. If you set replaysOnErrorSampleRate: 1.0, only sessions that error are recorded, which is usually 0.1–1% of all sessions. Most teams find replay cost-effective compared to the debugging time saved.

Can users opt out of session replay for privacy reasons?

Yes. You can check for a user's privacy preference and disable replay:

const privacyOptOut = localStorage.getItem("privacy_no_replay");
Sentry.init({
replaysSessionSampleRate: privacyOptOut ? 0 : 0.1,
});

What if I cannot use session replay for compliance reasons (HIPAA, GDPR)?

Disable replay entirely and rely on breadcrumbs and stack traces:

Sentry.init({
dsn: process.env.REACT_APP_SENTRY_DSN,
replaysSessionSampleRate: 0,
replaysOnErrorSampleRate: 0,
});

Breadcrumbs alone provide enough context to debug most issues without recording user interactions.

How long are breadcrumbs and replays retained?

By default, Sentry retains errors for 90 days and replays for 30 days. You can adjust this in project settings. Longer retention increases storage costs.

Can I view breadcrumbs without session replay?

Yes, absolutely. Breadcrumbs are always captured and shown on the error detail page. Session replay is optional and stored separately. Many teams use breadcrumbs alone for cost reasons.

Further Reading