Adobe Analytics Developer Expert study materials
Adobe Analytics Developer Expert study resources

The SPA Conversion Page That Never Fires

Adobe Analytics Developer Expert · Put your knowledge to work in context.

All case studies

The SPA Conversion Page That Never Fires

A retail client runs a React single-page application. The current Adobe Analytics implementation uses AppMeasurement with s.t() called on route changes via a global router hook. After migrating to Adobe Experience Platform Web SDK last quarter, the team reports that purchase events dropped 38% week-over-week while add-to-cart is flat. The confirmation page is a client-side route, not a full page load. The developer added alloy("sendEvent", ...) inside a useEffect in the confirmation component, but React 18 StrictMode double-invokes effects in development and the team is also seeing duplicate purchase events in staging. The XDM schema requires commerce.purchases.value and productListItems. The client's analytics lead wants to know why the drop occurred and how to fix duplicates without losing legitimate conversions.

1. Diagnose the most likely cause of the 38% drop in `purchase` events after the Web SDK migration, and explain why `add-to-cart` was unaffected.

Read the worked answer

add-to-cart fires from a component that mounts once and is triggered by a user click handler - a discrete, user-initiated action. purchase fires from a useEffect on the confirmation route, which is a client-side navigation with no document load.

Key reasoning:

  1. Timing vs. page unload. In the legacy AppMeasurement implementation, s.t() was likely called synchronously or with a beacon fallback on route change. Web SDK sendEvent is asynchronous and batched. If the user navigates away quickly (e.g., clicks a confirmation CTA, closes tab), the queued event may not flush before unload. Purchases are the terminal step where users leave fastest - add-to-cart users stay on the page.
  2. Effect not firing on all paths. If the confirmation component only renders on one route variant (e.g., /order/confirm but not /order/confirm/:id), an effect gated on mount may miss redirect-based confirmations.
  3. Identity stitching. If purchase relied on s.purchaseID for deduplication and that was dropped in the migration, the drop could actually be dedup suppression rather than lost events - verify by comparing raw hit counts, not just reports.

I would not accept 'the Web SDK is broken' as a diagnosis. The asymmetry between a click-driven event and a mount-driven event points to lifecycle and flush timing, not to the SDK itself.

2. The team wants to eliminate duplicate `purchase` events caused by React StrictMode. Propose a deduplication approach that is safe in production and explain the trade-offs.

Read the worked answer

StrictMode double-invocation is a development-only behavior, but the team is seeing duplicates in staging, which suggests either staging runs in dev mode or the effect also re-runs on legitimate re-renders. I would address both.

Approach 1 - Idempotent transaction ID (preferred). Generate a transactionID from the order confirmation payload (order number) and pass it in XDM. The SDK/Edge Network deduplicates on transaction ID within the configured window. This is safe because it is server-authoritative - the order number is unique per purchase regardless of how many times the component renders. Trade-off: requires the order ID to be available client-side and unique; if the client reuses order IDs across environments, dedup will silently drop legitimate events.

Approach 2 - Ref-based guard. Use a useRef flag set before the sendEvent call so the effect body runs once per component instance. Trade-off: refs reset on remount, so it does not protect against route re-entry or full remounts.

Approach 3 - Server-side confirmation. Fire the purchase from a server-side event forwarding property keyed on the order webhook. This is the most robust and also solves the unload problem from Task 1. Trade-off: requires Edge Network server API setup and identity stitching via ECID or authenticated ID.

Recommendation: Approach 1 as the immediate fix, Approach 3 as the durable architecture. Approach 2 alone is insufficient because it does not survive remounts, which is exactly the StrictMode failure mode.