Back to Blog Home

Session Replay: Becoming your own digital secret shopper

Kyle Tryon image

Kyle Tryon -

Session Replay: Becoming your own digital secret shopper

Session Replay: Becoming your own digital secret shopper

ON THIS PAGE

Retail stores have long relied on a secret weapon to measure and improve the shopping experience: the secret shopper.

Posing as ordinary customers, they evaluate the customer experience, spotting friction points like hard-to-find items, gauging the quality of customer service, and testing how seamless the checkout process feels. The data they capture acts as a proxy for unbiased, actionable feedback that shopkeepers use to enhance their customer’s experience, boost conversion rates, and increase average order value.

But in e-commerce, you're not limited to small sample sizes or proxy customers. With tools like Session Replay, you can analyze the journeys of real shoppers at scale — and even "replay" their sessions back to see exactly what they saw, click by click.

Enable Session Replay for real customer insights

Session Replay records a video-like reproduction of your user's experience, with all associated logs, errors, traces, and more attached. You'll be able to see what your users experience, while reviewing what was logged to the console at that time, and much more.

This is an excellent way to diagnose user issues without relying on them to file a report. Instead of asking for detailed descriptions, screenshots, or videos, you can simply see what happened. But it's more than just an error debugging tool, it can help you reveal customer behavior patterns — where users hesitate, where flows break down, and how design choices affect the journey.

To get started, you’ll want to select the correct SDK matching the front-end framework of your online store, and add Sentry with Replays enabled. If you are writing a Shopify app with Hydrogen, you can use the React Router SDK. Here’s a simple React setup that can be easily modified to fit your app.

Click to Copy
// import Sentry from your framework SDK (e.g. @sentry/react-router)
import * as Sentry from "@sentry/react";
Sentry.init({
  dsn: process.env.SENTRY_DSN
  // This sets the sample rate to be 10%. You may want this to be 100% while
  // in development and sample at a lower rate in production
  replaysSessionSampleRate: 0.1,
  // If the entire session is not sampled, use the below sample rate to sample
  // sessions when an error occurs.
  replaysOnErrorSampleRate: 1.0,
  integrations: [
    Sentry.replayIntegration({
      // Additional SDK configuration goes in here, for example:
      maskAllText: true,
      blockAllMedia: true,
    }),
  ],
});

You can set different capture rates for capturing Session Replays based on when there is an error, when you may want to capture a higher percentage of replays, and for general sessions. While experimenting, it’s a good idea to set these to 1.0 to make it easier to setup and debug, but you’ll want to lower these back down for production to put less strain on your monthly cap.

With Sentry configured, you can now start collecting telemetry data. Some framework SDKs (like Next.js) will automatically instrument error tracking and spans in much of your app. But you can always add more manual spans or metadata to any part of your app.

Diagnosing checkout drop-off and cart abandonment

Most e-commerce platforms track cart abandonment, showing you how many shoppers add items to their cart but never complete checkout. Knowing your checkout conversion rate is useful; knowing why customers abandon their carts is the knowledge you need to start closing the cart-checkout conversion gap.

You should always aim to maximize observability around the critical experiences in your applications. For online stores and shops, the checkout experience is the most critical point in your application. You can't make sales if users can't checkout.

Whether you're using Shopify, WooCommerce (via WordPress), or building your own custom checkout flow, you can implement comprehensive observability.

Adding observability to the checkout

The Sentry.startSpan method takes a callback function where you place any code you want to collect metrics around. Any code you execute here that has a Sentry span of its own will become a child-span of this parent, creating an easy-to-read flow of events to navigate in the Sentry Trace Explorer.

When you start collecting spans, you'll get a lot of metrics automatically, such as how long an operation takes, the user's web browser, and more. You can also attach your own custom attributes, like the total cart value. You may want to run a query in the future to show carts with a higher than average value.

Let's assume you're building a generic React app for this example. Your "cart" page is built as a .tsx component, with Sentry initialized via the useEffect hook to prevent re-triggering. You'll first immediately call setUser and provide an object containing the user's ID. If you're working on a Shopify app with Hydrogen, you can fetch the logged-in user via the Customer Account API.

Click to Copy
 const { cart, customerId } = useLoaderData<typeof loader>(),

 useEffect(() => {
	 // Set user context for all Sentry events
	 Sentry.setUser({ id: customerId });

   Sentry.startSpan({
      name: 'cart_view',
      op: 'commerce.cart.view',
      attributes: {
        // Use the actual data from the loader
        cart_items: cart.items.length,
        cart_value: cart.total.usd
      },
    }, () => {
      console.info(`🛒 Cart page viewed by user: ${customerId}`);
    });
  }, []);

Now, you'll be able to view the traces on Sentry for any given user that visits the cart page. You can add a few more spans around other critical parts of your cart page and the components within to get a more full picture.

Click to Copy
const handleCheckout = () => {
  Sentry.startSpan({
    name: "checkout",
    op: "commerce.checkout",
    attributes: {
	    cart_items: cart.items.length,
      cart_value: cart.total.usd,
      cart_shipping_cost: cart.calculateShipping()
    },
  }, async () => {
    try {
      console.log('🛍️ Checkout process initiated...');

      // This child span measures the payment processing step.
      await Sentry.startSpan({
        name: "Payment Processing",
        op: "commerce.payment.process",
      }, async () => {
        console.log('💳 Processing payment...');
      });

      // This child span measures the order creation step.
      await Sentry.startSpan({
        name: "Order Creation",
        op: "commerce.order.create",
      }, async () => {
        console.log('📋 Creating order in the database...');
      });

      console.log('🎉 Checkout completed successfully!');

    } catch (error) {
      console.error('❌ Checkout failed:', error);
      Sentry.captureException(error);
    }
  });
};

Track abandoned carts

As I said, most platforms have some kind of built-in system for identifying and tracking "abandoned" carts. The definition of what an abandoned cart actually is  will vary from platform to platform. If you're building your own app using platform APIs, you can define and track abandoned carts on any criteria you choose.

Shopify has an abandoned checkout tracker, but it's tied to their external checkout page where you lose visibility, and the information becomes less useful. Even so, Shopify only counts a cart as abandoned if the user reaches checkout and enters their email before leaving. You may have a different idea of that that means for our bespoke app.

If you have your own app with your own cart checkout page, you'll want to see how customers are interacting with it. So for your custom app, you'll need a custom solution.

You can store cart information server-side, using the session ID for the temporary customerId, so it's possible to record and analyze user behavior patterns even before they complete checkout. This is also necessary if you plan to define an abandoned cart as a cart that hasn't checked out over a longer period of time than a single session.

However you define and build your app to track abandoned carts, your goal is to be able to retrieve a list of customerIds with abandoned carts, so you can later investigate those users' sessions in Sentry.

Your server-side code might look something like this:

Click to Copy
// Server-side cart tracking (Node.js/Express example)
const abandonedCartTracker = {
  // Track when a cart is created or updated
  updateCart: async (sessionId, cartData) => {
    await db.carts.upsert({
      sessionId,
      customerId: cartData.customerId || sessionId, // Use sessionId as fallback
      items: cartData.items,
      totalValue: cartData.total,
      lastUpdated: new Date(),
      status: 'active'
    });
  },

  // Mark carts as abandoned after 30 minutes of inactivity
  // Run this on a cron job (every 15-30 minutes) or trigger it when users visit your site
  markAbandonedCarts: async () => {
    const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);

    return await db.carts.updateMany(
      {
        lastUpdated: { $lt: thirtyMinutesAgo },
        status: 'active'
      },
      { status: 'abandoned' }
    );
  },

  // Get list of abandoned cart customerIds for Sentry investigation
  getAbandonedCartUsers: async (timeframe = '24h') => {
    const timeframeCutoff = new Date(Date.now() - (24 * 60 * 60 * 1000));

    const abandonedCarts = await db.carts.find({
      status: 'abandoned',
      lastUpdated: { $gte: timeframeCutoff },
      totalValue: { $gt: 50 } // Focus on higher-value abandoned carts
    });

    return abandonedCarts.map(cart => ({
      customerId: cart.customerId,
      cartValue: cart.totalValue,
      itemCount: cart.items.length,
      abandonedAt: cart.lastUpdated
    }));
  }
};

With this data, you can then search Sentry for specific user sessions to see exactly what happened during their checkout process.

Querying Sentry for abandoned carts with Session Replays

Now you know which users (temporary or not) currently have "abandoned" carts, however you have defined them, and you know the customer's ID that matches the ID you set with setUser in your Sentry spans.

Navigate to the Trace Explorer and query for spans that contain your user's ID, the cart page, and contains a session replay:

Click to Copy
user.id:guest span.description:/cart has:replayId

Replace guest with the actual customerId from your abandoned cart list. This query will show you all traces where:

  • The user ID matches your abandoned cart customer

  • The span involves the cart page (/cart)

  • A session replay is attached (has:replayId)

If you look at the “Traces Samples” tab, you can have your results aggregated by trace. You can see here there were 4 separate events where the user navigated to the /cart page. You can click into any of these traces to see the full set of spans that were captured in that transaction.

After clicking a trace, you’ll be presented with a waterfall view of spans. Because distributed tracing is enabled here, you can see spans from both the front end and backend of this particular application.

The span that is highlighted by default is the one that matches the query you ran a moment ago, and it contains the custom attributes you attached, like the user’s ID. You’ll be able to see those attributes on the right-side panel.

In that panel, if you scroll down past the attributes, you’ll find the attached Session Replay.

Click the “See Full Replay” button, and we’ll take a look at this user’s session, and see if you can learn anything about their experience that may indicate why they abandoned the cart. You should be on the look out for things like slow loading times, layout shifts, and errors.

Reviewing abandoned carts with Session Replay

Now comes the most valuable part: watching the actual user experience that led to cart abandonment. When you click on a Session Replay, you'll see exactly what your customer saw, experienced, and struggled with.

Session replays are not videos, but allow you to replay a user’s DOM in a video-like experience. In the right panel you see there are a few tabs: errors, network, console, and breadcrumbs.

Errors are Sentry’s flagship product, and usually where you start when debugging the user experience. In this case though, the user experienced no errors. At least none that were captured.

At the bottom of the player is the play button, and you can see on the timeline there are small blips. Each of these corresponds to an event on the page, like a click, or an error is returned. The color of the blip will indicate what type of event it is, and you can hover over it to learn more. They will also appear with more detail in the Breadcrumbs tab.

Video thumbnail

In this case, the user probably didn’t leave because of an error or slow load times. In fact, no metric you could have measured would have flagged the issue.

The replay shows the user navigate to the cart page, and then enter their country and zip code to calculate shipping. The total changes from $4.99 to $13.98 after shipping. A moment later, the cursor darts to the top of the screen and the session ends.

After review, it seems the user likely intended to complete their purchase, but their activity suggests they were not willing to pay the 3.5x price increase.

Next steps for your store

Cart abandonment isn’t just a number in your analytics dashboard, it’s a reflection of the customer experience and a leak where potential revenue slips away.

Human behavior isn’t always quantifiable with metrics. You can make assumptions from telemetry, and you might collect limited feedback through surveys or support tickets. But proxy data for customer’s experiences will never be as revealing as watching what actually happened in the moment.

With Session Replay, you move beyond guesswork. You see the real journey — every click, scroll, and hesitation, paired directly to the spans and events that shaped it.

Session Replay works for any and all web applications, and even iOS and Android apps. Ensure you have full visibility into your customer experience. You can more about how to configure Session Replay and its many available options in the Sentry documentation.

When dashboards and numbers aren’t enough to explain human behavior, a set of eyes might be all you need.

Share

Share on Twitter
Share on Bluesky
Share on HackerNews
Share on LinkedIn

Published

Sentry Sign Up CTA

Code breaks, fix it faster

Sign up for Sentry and monitor your application in minutes.

Try Sentry Free

Topics

Error Monitoring
How Anthropic solved scaling log volume with Sentry

How Anthropic solved scaling log volume with Sentry

[Upcoming Workshop] Fixing Your Frontend: Performance Monitoring Best Practices

[Upcoming Workshop] Fixing Your Frontend: Performance Monitoring Best Practices

Listen to the Syntax Podcast

Of course we sponsor a developer podcast. Check it out on your favorite listening platform.

Listen To Syntax
© 2025 • Sentry is a registered Trademark of Functional Software, Inc.