Try for free

Building a Personalized User Experience for Cart Recovery

July 06, 2025

In one of our previous blog posts we described how to customize Cart Recovery to completely change the user experience to match the website using it. It worked well enough for most cases, but the mechanism for overriding the UI implementation was causing trouble for highly dynamic use cases, such as when components were loaded asynchronously and needed to initialize after the tag had already been loaded.

With the recent introduction of the ready event handler we've decided to reconsider how custom UIs could be built for ShopWallet in a more straightforward and reliable way.

Timing is Everything

Before the introduction of the ready event custom code had to be loaded before the /init call, else it would not correctly override the necessary functions that would trigger the custom UI. Now that we had a mechanism to reliably call functions when both the tag has been initialized and the custom code loaded, we could expose the parameters and APIs needed for ShopWallet UI to work.

As with the site personalization, the ready callback receives all of the init information as the parameters:

edgetag('ready', ({ userId, isNewUser, ...rest }) => {
  // ...
})

For a Cart Recovery UI implementation, we won't need those parameters unless we want to use them to customize the user experience for the user somehow.

For the purposes of this example, let's use the example from another blog post that just uses the native dialogs built into the browser to present the UI:

edgetag('ready', async () => {
  // we can access the APIs and variables we need from the registry key
  // defined on the global object
  const { walletAPI, storeAPI, variables } =
    window[Symbol.for('blotout-wallet')]
 
  if (!variables.enabled) {
    // if the current user is not enabled, do not run recovery
    return
  }
 
  try {
    // fetch expired carts, if any
    const expiredCarts = await walletAPI.getExpiredCarts()
    if (expiredCarts.carts.length) {
      // grab the latest expired cart
      const lastExpiredCart = expiredCarts.carts.at(0)
      // prompt the user to restore or delete the cart (cancel will delete)
      if (confirm('Expired cart found! Restore?')) {
        // adds items to the cart in your shop
        await storeAPI.addItems(lastExpiredCart.items)
        // marks the cart as restored in the Wallet database
        await walletAPI.restoreCart(lastExpiredCart.cartId)
        alert('Cart restored!')
      } else {
        await walletAPI.deleteCarts()
        alert('Cart deleted!')
      }
    }
  } catch (e) {
    console.error(e)
  }
})

The walletAPI and storeAPI are identical to the ones described in the previous examples, but variables has been introduced as an object describing the parameters configured in the Cart Recovery provider form and lets you use the configuration specified in order to control your implementation right from the EdgeTag app - no need to deploy or change your code.

Controlling Popup Behavior

The variables parameter contains the following properties:

enabled (boolean)

Whether the current user can restore carts. Useful in cases when A/B testing or preview mode is used.

experiment (object)

Describes the current user's group and mode of operation.

silentRestore (boolean)

Returns the value set in the channel's provider form - when the value is true the expectation is that the cart should be restored without the user having to confirm restoration.

afterRestore (object)

Describes the action configured in the channel's provider form. Based on the value, the UI can perform the configured action.

The main advantage to implementing a UI that takes all these parameters into account is the ability to update these and other parameters through the provider form in EdgeTag, which removes the necessity of timing the changes to the channel with your code deployment.

Ready to Roll

Once you have your UI code ready, you can configure the Cart Recovery channel to exclude the default UI bundle. To do that, simply enable the checkbox in your Cart Recovery channel provider form:

The checkbox in the Cart Recovery provider form that prevents the default UI code from being bundled with the tag code

This will exclude the default UI and prevent it from appearing on the page, letting you control the UX.

✋ Important: there is one rule to follow for smooth operation - cart recovery will only work if the current user is enabled. If Cart Recovery is running in disabled, ab-test or preview mode, the current user's enabled flag will depend on the mode of operation, the group the user belongs to or whether or not they have a preview key set. You can decide what kind of UX to present to users that are not enabled, or none at all.