In Q4 2025, we began working with a B2B beverage distributor — a Shopify Plus merchant doing approximately $8 million ARR supplying specialty spirits, craft beers, and premium mixers to bars, restaurants, and hospitality groups across the American Southwest. Their catalogue ran to roughly 1,200 active SKUs, their average order value hovered around $148, and their most important buyers were operations managers and bar managers placing reorders on mobile devices between service shifts. That last detail — mobile-heavy, time-constrained buyers — is what makes the performance degradation we discovered so commercially damaging. These were not casual browsers. They were professionals trying to complete a task quickly, and the cart page was making that impossible.
The challenge
The client's mobile checkout abandonment rate was 22% — approximately 8 percentage points above their own historical baseline from two years prior, and well above the 13-15% industry average for B2B mobile commerce. Their analytics data showed a clear behavioural pattern: buyers who reached the cart page were abandoning between cart view and checkout initiation, not within the checkout flow itself. This narrowed the diagnosis to something happening in the cart page render, not in the payment or shipping steps.
A performance audit using WebPageTest from a mobile device on a throttled 4G connection revealed the root cause immediately: three separate third-party upsell apps were injecting JavaScript into the cart page. Each app polled the cart REST API on page load to evaluate which upsell offers to show. On a 4G mobile connection, the sequence was devastating: HTML loaded, then three sequential or semi-parallel JS fetches fired, each waiting for cart API data before rendering their respective widgets. Time to first meaningful interaction on mobile averaged 4.8 seconds from the initial navigation. On slower rural 4G connections — common in the areas where many of the client's hospitality customers operated — it exceeded 7 seconds.
The apps themselves were not low-quality — they were doing what their architecture required. Client-side JavaScript upsell evaluation needs the cart data to assess which offers apply, and in 2022-era Shopify, client-side JavaScript was the only viable option. By Q4 2025, with Shopify Functions and Checkout UI Extensions generally available and battle-tested, that architectural constraint was gone. The client was paying a performance tax for an architecture era that had ended. The abandonment spike aligned closely with the installation dates of the three apps, which had been added incrementally over 18 months as the marketing team discovered new upsell use cases.
The solution
We proposed migrating the upsell logic entirely off client-side JavaScript and onto a Shopify Function using the cart_transform Function type, paired with TOPCODE Upsell 2.0's Checkout UI Extension for presentation. The architectural shift was fundamental: all three legacy apps evaluated offers in the browser after page load. Moving offer evaluation server-side — inside a Function that runs as part of cart assembly before the HTTP response is returned — meant the cart page could render fully without waiting for any upsell evaluation to complete. The offer data would arrive embedded in the cart object, not fetched asynchronously afterward.
The implementation used a cart_transform Function to read the cart lines and the buyer's company location (via buyerIdentity.companyLocation) and emit upsell recommendations as cart metafields. The Checkout UI Extension reads those metafields at render time — no additional API call required, no JavaScript fetch loop. The cart page HTML arrives from the server with the upsell data already embedded in the cart object's metafields. First meaningful interaction time dropped because there was no async evaluation step at all.
Rule configuration — which SKUs trigger which upsell offers, minimum quantity thresholds, B2B-specific offers for certain company account tiers, case-pack upsells for hospitality volume buyers — was migrated to metafields on the Function's discount node, editable by the client's merchandising team through a simple App Bridge interface. This was a hard requirement: the team ran weekly promotions that cycled upsell offers, and requiring a developer deployment for every rule change was not operationally viable. The Function reads the metafield JSON at evaluation time, so any rule change takes effect on the next cart load with no code deploy.
The migration executed across three two-week sprints. Sprint 1: audit and document all existing upsell rules across the three legacy apps (which used incompatible rule schemas — one tag-based, one product-metafield-based, one using a proprietary CSV format), identify redundancies and contradictions, build the unified rule engine in TypeScript targeting the Functions API. Sprint 2: build and QA the Checkout UI Extension, deploy the Function to a development store, A/B test the new system against the legacy apps on 20% of traffic for two weeks using a cart attribute flag to control routing. Sprint 3: validate A/B results, roll to 100% traffic, remove all three legacy apps, clean up the theme template of residual script tags, and confirm Core Web Vitals improvement in Google Search Console.
Checkout time (mobile)
3.0s
from 4.8 s
-37.5%
Mobile checkout abandonment
13%
from 22 %
-40.9%
Average order value
176$
from 148 $
+18.9%
Cart page TTFB
390ms
from 640 ms
-39.1%
The abandonment reduction from 22% to 13% represented roughly $380,000 in annual recovered revenue at their existing traffic volumes, calculated from the incremental orders that would have been abandoned. The AOV improvement — $148 to $176 — was attributable to better upsell presentation (the UI extension rendered reliably before buyers reached checkout, rather than sometimes appearing after they had already committed to their cart) and to a consolidation of upsell rules that eliminated contradictory offers that had been confusing buyers.
What we'd do differently
The A/B testing setup in Sprint 2 was more complicated than it needed to be. We built a custom split-testing mechanism using a cart attribute flag to route buyers to either the legacy or new upsell experience, then analysed session data manually to attribute outcomes to each cohort. In retrospect, TOPCODE Upsell 2.0's built-in A/B testing would have been simpler to instrument and would have given us better statistical confidence on the AOV and abandonment metrics. The lesson: evaluate the full feature set of the tools you are deploying before building custom infrastructure around them.
We also spent more time than expected on the rule migration audit. The three legacy apps used incompatible schemas, and the process of validating that each new rule faithfully reproduced the intent of the old rules (not just the syntax) took most of Sprint 1. Going forward, any multi-app upsell consolidation project should plan two full weeks for rule archaeology before any code is written. Bring a product person from the merchant's team into that process — they are the only ones who can confirm what each rule was originally intended to achieve, not just what it is currently configured to do.
Finally, we did not initially scope the B2B-specific upsell logic in the project brief. The client had retail buyers and B2B company location buyers, and those segments should have entirely different upsell patterns — a restaurant buyer reordering house spirits should see case-quantity upsells; a retail buyer should see complementary mixers. We caught this gap in Sprint 2 QA when the B2B test cohort received retail-style upsell offers and several company buyers complained via support tickets. It caused a one-sprint delay to implement the B2B-aware rule evaluation. Lesson: always inventory buyer segments and their expected experiences at project kickoff when working with Plus stores that serve both retail and B2B channels.
Takeaways for other Plus merchants
Client-side upsell apps are a performance tax you pay on every single cart page load. If you are on Shopify Plus and running more than one JavaScript-heavy upsell or personalisation app, run a WebPageTest audit from a mobile device on a throttled 4G connection before reading this sentence any further. If your cart page time-to-interactive exceeds 3 seconds, you have a conversion problem disguised as an app problem. The fix is architectural — moving evaluation server-side — not a matter of finding a faster JavaScript app.
Shopify Functions plus Checkout UI Extensions is the right architecture for server-side offer logic. The Function evaluates offers server-side as part of cart assembly — zero client-side execution cost. The UI Extension renders the evaluated result at checkout using Shopify-optimised JavaScript. The combination eliminates the render-blocking problem at the source. It is also the only approach that is guaranteed to remain compatible with future Shopify checkout updates.
Performance work and conversion work are the same track. The 18.9% AOV lift in this engagement was not primarily from more persuasive upsell copy or better offer selection — it was from the fact that buyers were no longer abandoning before the upsell rendered. The most sophisticated merchandising logic in the world produces zero incremental revenue if it never displays to a buyer because they left before the JavaScript finished evaluating. Speed is the first requirement of any conversion optimisation work.
