AJAX Cart Drawer and Line-Item Rules Without an App
Definition
A slide cart built on Shopify's cart AJAX API and the Section Rendering API: line items, discounts, upsells and the header count bubble all re-render in one round trip, with no page load and no cart app. Includes the line-item level control that bundles, subscriptions and gifts demand — fixed-quantity SKUs, hidden components, locked promotional lines and file uploads.
The Evidence
- 16 live, Deploi-attributed builds across 3 Shopify storefronts — Nudestix 9, LUS Brands 4, Three Ships 3.
- Attribution: 9 Deploi-authored, 7 built on top of a vendor theme and disclosed as such on their own pages.
- Status: live, verified 2026-09-06.
- Evidence quality: eight carry code plus client working records. Eight are documented from the code; no client-side record of the decision survives.
- Confidence: 7 strong, 9 moderate.
- Not counted: twenty-one further records carry this capability and are vendor-derived, legacy, no longer live, or held back from this batch. Not listed, not claimed.
- Five of the 16 have published build pages.
How It's Actually Built
One round trip does both jobs. The cart AJAX API changes the cart and returns cart state; it returns no markup. So the request carries a sections= list and Shopify hands back re-rendered section HTML in the same response that performed the mutation. The theme swaps those fragments in.
That forces an architecture. The Section Rendering API returns whole sections, so every fragment that updates independently has to be its own section file — cart-icon-bubble, cart-live-region-text, cart-notification-product. Their filenames are a contract: the cart JS names them in getSectionsToRender().
State is derived on the server, every render. A drawer that replaces its own inner HTML destroys every element and listener inside it, so nothing important is kept in JavaScript memory. Threshold progress, gift eligibility, protected-line status and points-per-line are computed in Liquid from cart.total_price and the line items on each render, and the HTML that comes back is self-describing — the JS reads markers the server wrote ([data-free-sample-remove], [data-protected-gwp-quantity]) instead of remembering what it did. That is what keeps the cart correct after a reload, a back-button restore, or a change made on another surface.
Only what genuinely needs cross-render memory lives in JS. A progress bar rebuilt from scratch has no previous width to animate from, so the previous percentage is parked outside the element and re-applied with the transition suppressed for one reflow.
Line-item rules ride on properties and metafields. Leading-underscore properties (_bundleId, _is_free_product, _product_meta) stay out of the customer-facing cart display but reach the order — that's how a set assembled on a collection page stays editable and how a gift line stays identifiable downstream. Per-SKU rules come from product metafields read in the same Liquid pass, so a line's controls are decided by merchandising data rather than by the script that drew the row, and a protected line keeps a hidden updates[] input so a no-JS form post can't zero it.
And the buy control is a view, never a second form. A sticky bar mirrors the real product form's quantity, label and disabled state and forwards taps into it, so variant rules, quantity rules and error handling keep one implementation.
The Buy-vs-Build Position
Built on all three storefronts with no cart app; this is the clearest repeat BUILD in the corpus.
Read that literally. It doesn't mean a cart app is the wrong purchase. It means that on these three catalogs the drawer had to carry rules the merchandising team edits weekly, and rules of that shape are cheaper in the same Liquid pass that renders the drawer than in a second admin somebody has to keep in sync. The cost argument, including the stores where the app is the right call, is in the slide cart and sticky add-to-cart decision.
The Builds
Nudestix — Dawn 6.0.2
- Cart page rebuilt around loyalty points, protected gifts and a dark order summary — per-line points derived in Liquid, gift lines that resist re-quantifying, all of it recomputed inside the section-rendering loop. High, cart.
- A collection template turned into a palette builder that carts one priced SKU — the shopper assembles a set on a collection page; the picks travel as line-item properties on a single priced SKU. High, collection and cart.
LUS Brands — Dawn 15.4.1
- Mobile sticky add-to-cart bar mirrored from the real product form — a bar portaled to
<body>that shadows the form rather than duplicating it. High, product detail page. - 'Bundle and save' PDP block driven by a product-list metafield — a curated set on the product page where each card carries a real
{% form 'product' %}inside Dawn's<product-form>, so an add from a cross-sell card takes the same route and re-renders the same drawer as the main buy button. Medium, product detail page.
Three Ships — Palo Alto 5.8.0 by Presidio Creative
- Tabbed collection product slider and the shared merchandising card — curated rails whose in-grid add carries the same line-item payload a product page add would. Medium, home and landing.
Eleven more builds count toward the evidence above. Seven have no page written yet. Four won't get one: two are earlier generations of a builder already published here, and two stopped at editorial review.
What This Rests On
- Cart AJAX API — in 14 of the 16; the mutation itself.
- Liquid section schema and settings — in 10; where merchandisers set thresholds, copy and gift products.
- Section Rendering API — in 8; the markup that comes back with the mutation.
- Metafields — in 7; per-SKU and per-collection rules that outlive a deploy.
- Custom elements — in 4; the drawer, tray and form elements the logic runs inside.
- Selling plans — in 4; subscription lines rendered differently from one-time lines.
What Varies by Storefront
The split — 9, 4 and 3 — tracks where each business runs its cart, and the theme base sets the ceiling.
Nudestix runs Dawn 6.0.2, and almost the whole average-order-value programme lives in the cart: spend thresholds, gift lines the shopper must not delete, a points figure per line derived in Liquid from the tier tag the loyalty app writes. Nine builds is what it costs to make a drawer carry commercial rules that change monthly.
LUS Brands runs Dawn 15.4.1, which already re-renders its own drawer. So the work sits at the edges: incentives rendered inside the drawer, a sticky buy control outside it, and the three small section fragments the cart JS re-fetches by name. Four builds, none of them a cart.
Three Ships runs Palo Alto 5.8.0 by Presidio Creative, a paid premium theme, and the cart there is largely owned by an installed recommendation app. So the line-item work is different in kind: guards written inside the vendor's own line renderer for fixed-unit SKUs and app-injected lines, plus the code that keeps the drawer in step after a mutation the theme did not make. Three builds, and the interesting ones are on the vendor widget remediation hub instead.
Where This Gets Hard
Nothing you render sets a price. Shopify gives the theme no free-gift primitive, so a gift is an ordinary line and the money comes from a discount rule, a Function or an app that sits outside the theme entirely. The drawer renders the state that rule produces; it is never the rule. So the sequencing matters more than the markup — settle where the money is enforced, then build the drawer that shows it. We have never regretted asking that question first.
You inherit the whole lifecycle. Sold-out variants, a double tap during an in-flight add, a back-button restore, a mutation made on another surface — every state an app would have owned is now yours to render. That's the real price of building it, and it comes due in edge cases rather than in the first version.
We've solved this three times and it's still three implementations. Sixteen builds across three storefronts exist to keep three carts correct, and the threshold and gift-line logic in particular was re-derived on each one rather than carried across — different theme base, different rules, the same problem. There is no component here we hand you off the shelf. The second store costs less than the first, and not by as much as you'd hope.
Two cases where we'd tell you not to. Where the cart is already owned by an app the store depends on for merchandising: adding a second system that writes to the same cart is the expensive move, not the cheap one. And where the rule belongs at checkout and your plan can run a Function — put it there. A Liquid pass can only ever describe a discount; a Function is the discount.
Related Capabilities
- Vendor widget remediation and cross-app cart sync — when more than one system holds the cart.
- Fixed bundles and kit merchandising — the set-building side of the same problem.
- Collection merchandising: rails, tiles and in-grid promotion — adding to cart from a grid.
- Variant swatches and shade pickers — choosing the variant the cart is about to receive.
Answers to Help You Make Smart Decisions
Do you need an app to build a slide-out cart on Shopify?
No. The cart AJAX API performs the mutation and the Section Rendering API returns re-rendered section HTML in the same request, so a theme can update the drawer, the header count and the live region without a page load. Sixteen live builds across these three storefronts do exactly that. What you take on is the lifecycle the app would have owned.
How do free-shipping bars and gift-with-purchase work without an app?
Progress and eligibility are computed in Liquid from cart.total_price on every render, so the correct state is in the drawer's first paint and in every section-rendering response. Thresholds, copy and gift products are ordinary theme settings a merchandiser edits. The price of the gift line itself comes from a discount rule outside the theme.
What happens to a theme-built cart when the shopper reloads or hits back?
If the state is derived server-side, nothing happens — the page re-renders and the rules recompute from the cart. That is the whole reason these builds keep eligibility in Liquid rather than in JavaScript memory. Any rule you hold only in the browser is a rule that disappears when the drawer replaces its own contents.
Can a theme cart coexist with a cart app that's already installed?
Yes, and it's common. The theme keeps its own line-item rules in the Liquid that renders the row — the merchandising data a line is governed by, the property that marks an app-injected line — and listens for the app's cart-change event to re-render from server truth. That coexistence work is its own capability, and it has a cost worth forecasting.
Ready to Put Your Cart's Rules in Your Own Theme?
We build slide carts, thresholds and line-item rules on Shopify's own APIs, inside your theme, with the merchandising controls in the theme editor where your team already works. Contact us today to talk about what your cart is supposed to enforce.