Skip to main content

Building a Custom Astro Shopping Cart

Tony Cooper 15 min read ecommerce
Building a Custom Astro Shopping Cart
I built a custom shopping cart on Astro for a real, live ecommerce site — indoorluxury.co.uk in my own portfolio. No Shopify. No Hydrogen. No hosted commerce platform. Here’s the architecture, what surprised me, and why I’d still recommend Shopify to nearly every client who asks.

The temptation to build your own shopping cart on a modern framework is real. Astro is fast. Stripe is straightforward. You read the documentation, you sketch the data model, you write a few thousand lines of code, and suddenly you’re not paying anyone monthly platform fees. The control is intoxicating.

I built one. It works. And the lesson, told honestly, is that for almost every merchant reading this, you should still be on Shopify.

This piece is the long version of why. The architecture of what I built, the parts I’m proud of, the parts that still bother me, and a clear framework for when the trade is genuinely worth making versus when it’s an expensive distraction from running an actual business.

Why I Built This Instead of Using Shopify

I own Indoor Luxury — a luxury indoor furniture site in my Empire portfolio. Not a client engagement. My property, my time, my decision about what stack to ship on.

That distinction matters because it gives me three things most merchants don’t have:

  • Permission to experiment. Nobody is paying me hourly while I learn something. The build cost is my own time. If it fails I lose evenings, not client trust.
  • A simple commerce surface. Luxury furniture dropship has unusually clean commerce mechanics. No subscriptions. No B2B catalogue. No complex tax across regions. No marketplace-style multi-vendor flows. UK-only at launch. That makes a custom build feasible in a way it wouldn’t be for, say, a subscription wellness brand selling internationally.
  • A reason beyond cost. I wanted to know what was at the edge. To be able to advise clients on platforms having actually built the alternative — not just having read other people’s opinions about it. Authority earns the right to recommend Shopify by having walked the other path.

If you don’t have all three, I’ll get to the recommendation later in this piece. The short version: stay on Shopify. The long version requires understanding what’s actually in the box you’d be opting out of.

The Architecture — What TonyCart Actually Is

I named the cart layer TonyCart because the goal from day one was reusability across the seven-property Empire portfolio, not a one-off build for one site. It lives as a workspace package (@wbs/patterns) inside the monorepo, and Indoor Luxury is just the first site importing from it.

The whole cart and checkout layer is around 4,800 lines of code: roughly 3,000 lines of cart components (cart drawer, product card, product actions, gallery, recommendations, quantity selector, recently-viewed, wishlist), and 1,600 lines of checkout components (form, order summary, confirmation page, client orchestration script, Stripe payment intent server function, and the Stripe webhook handler that handles order plumbing after payment).

4,800
Lines of code across the cart and checkout layer

That number is worth pausing on. Shopify replaces all of it. So does WooCommerce. So does BigCommerce. When you’re considering a custom build, you’re considering writing and maintaining 4,800 lines of code that already exist, are battle-tested, and are someone else’s problem to keep working.

The Cart State Model

Cart state lives in localStorage, not on a server. This is a deliberate choice with real consequences in both directions.

What it buys you: instant load times. No round trip to a server to check what’s in someone’s basket. No database. No session management. No auth required to add items. The cart works for unauthenticated visitors and persists across visits without any backend infrastructure at all. On a static Astro site, this matches the deployment model — files on a CDN, no server, no fees.

What it costs you: no abandoned cart recovery without bespoke wiring. localStorage is per-device, per-browser. If a customer adds £2,000 of furniture and then leaves the site, there’s no way to email them later unless I’ve already captured their email separately. Shopify gets this for free. I’d have to build it from scratch — email capture earlier in the flow, a serverless function to write the abandoned cart to a database, an ESP integration to trigger the recovery email, and a sign-in mechanism to restore the cart when they click back.

I haven’t built that yet. It’s on the list.

A small thing I’m quietly proud of

The cart synchronises across browser tabs. If a customer has the cart open in two tabs and adds an item in one, the other tab’s cart icon updates immediately. This is done via the browser’s storage event — when localStorage changes in one tab, every other tab on the same origin fires a storage event, and the cart listens for it and dispatches its own cart-updated event.

Most shopping carts ignore this. Shopify ignores this. The bug isn’t visible until a customer hits it, and then it looks like a frustrating glitch — items “disappearing” from the basket because the customer is now on the wrong tab.

It’s about ten lines of code. It took me ten minutes to write. It’s the kind of detail you only notice when you’re the one writing the cart.

The Checkout Layer

The checkout uses Stripe Payment Intents, not Stripe Checkout Sessions. The difference matters.

Checkout Sessions are the simpler integration — you create a session on the server, redirect the customer to a Stripe-hosted checkout page, and Stripe handles everything until they return to your site. This is what most Astro Stripe tutorials use. It’s easier to build and ships in an afternoon.

Payment Intents are the harder integration. Stripe gives you a client-side card element, you write the checkout UI yourself, and you orchestrate the payment confirmation flow on your own page. The customer never leaves your site. The trade-off is several hundred lines of client-side JavaScript handling form validation, postcode-based shipping calculation, payment confirmation, 3D Secure redirects, error states, and the post-payment journey to the confirmation page.

I chose Payment Intents because the customer never leaving your site is a genuine conversion difference on a luxury furniture purchase, where trust signals matter and a redirect to a Stripe-hosted page feels slightly off-brand. But it’s far more work. Most clients who ask me about custom Astro checkouts underestimate this. I’d not recommend Payment Intents unless the brand reason for keeping the customer on-site is real.

Shipping — The Postcode Zone Engine

This is the part that connects to a different piece of writing on this site. I built UK postcode-based shipping directly into the checkout client.

Three zones: Mainland UK (free delivery), Scottish Highlands & Islands (surcharge), Northern Ireland / Isle of Man / Isles of Scilly (surcharge). The postcode validation is regex-based and the zone detection is two array lookups — straightforward to write, but the data — the actual postcode lists that define each zone — came directly from the work I did on the UK Shopify Shipping cluster where I documented exactly the same logic for Shopify merchants using the Parcelify app.

That cross-pollination is one of the unexpected gifts of building things from first principles. The work compounds across the portfolio. The postcode zone logic that took weeks to research and document for Shopify clients took ten minutes to port into the Astro cart, because the underlying knowledge was already mine. The platform changes; the postcodes don’t.

What I’m Proud Of

A short list of decisions that turned out right.

  • Pattern library, not per-site build. The cart lives once and ships everywhere. When I improve the cart drawer, every Empire property gets the improvement on the next deploy. This wouldn’t be true if I’d built it inline in Indoor Luxury.
  • Money in pence, integer everywhere. Floating-point arithmetic on money is the source of countless ecommerce bugs. The cart stores prices as integers (pence), arithmetic happens on integers, and display formatting happens via Intl.NumberFormat at the boundary. Nothing is ever £19.989999996.
  • Three subsystems, one library. Cart, wishlist, recently-viewed. They share patterns (localStorage, custom events, cross-tab sync) but stay logically separate. A customer can add things to wishlist without an account, move them to cart later, and the recently-viewed list builds up automatically as a soft recommendation engine.
  • Custom events as the integration surface. Components communicate via dispatched events (cart-updated, wishlist-updated, recently-viewed-updated), not direct function calls. Any component can listen to any event. Adding a new component that responds to cart changes is straightforward — no rewiring of existing components required.
  • The Stripe webhook handler. Five hundred lines of order-fulfilment plumbing — payment success, failure, refund handling, email confirmation triggers, supplier dispatch notifications, audit trail logging. It’s unglamorous code, but it’s the part that makes the difference between a checkout that processes payments and a checkout that runs a business.

What This Build Makes Possible

The flip side of the trade-offs is worth naming. These are real commercial advantages that platform-bound merchants can’t reach.

  • Performance on every page, by default. Astro ships zero JavaScript unless you opt in. Product pages, category pages, content pages all serve pure HTML from an edge CDN. Indoor Luxury’s product pages score 95+ on mobile Lighthouse without any optimisation work. Shopify themes carry framework overhead that’s there whether you need it or not.
  • Content-commerce blending. Every page can be commerce, every page is content-first. A buying guide can have a “shop this look” carousel that draws from the same product catalogue as the storefront. A blog post can carry a real cart trigger. The line between editorial and commerce stops existing.
  • No growth tax. The inverse of EKM’s £100/mo-per-£100k-turnover escalator. Stripe takes a transaction fee, Netlify hosts the static site for pennies, and the bill doesn’t change as the business grows. A merchant doing £1m through TonyCart pays the same hosting bill as one doing £100k.
  • Reusability across the portfolio. TonyCart is a workspace package. It powers Indoor Luxury today, and the same library will power the other Empire properties as they go live. One cart, seven storefronts. Every improvement compounds across all of them.
  • True ownership of every line. No Liquid template limits. No app-marketplace dependency. No theme version that Shopify might deprecate. Every customisation is possible because the code is mine — including, importantly, the decision to not customise something and keep the build maintainable.
  • AI-era storefront flexibility. The storefront is plain HTML on the edge — the same surface AI models read when reasoning about products. As AI-driven commerce becomes a real channel (LLM-mediated shopping, conversational interfaces, agentic checkout), a custom Astro storefront has options that platform-bound stores don’t.

The Roadmap — What’s Still to Build

The cart is shipping and iterating. These are next on the list.

  • Abandoned cart recovery. Email capture at checkout step one, serverless function to persist, ESP integration to trigger the recovery email, restore link to rehydrate the cart. The framework is there; this is build-it work.
  • Multi-currency. Indoor Luxury is UK-only by design today. When the catalogue is ready for EU or US customers, currency display + geo-detection + tax-per-region + shipping-per-destination is the next architectural pass. Months of work, but the foundations are in place.
  • Wider integration coverage. Klaviyo is wired in for newsletter and post-purchase. Reviews integration (Trustpilot or Loox), loyalty programmes, post-purchase upsells, returns workflow — each one is a bespoke build, but each one only happens once and lives in the pattern library for every Empire property.
  • Ongoing platform discipline. Stripe API changes, Astro version bumps, regulation updates (PSD2, accessibility, EU compliance). On Shopify, an engineering team handles all of this for you. On TonyCart, this is part of running the system — and the discipline of doing it well is itself the practitioner edge.
  • 3D Secure refinement. I built the redirect-and-return choreography and it works. The next iteration tightens the edge cases and adds better fallback handling for the small fraction of UK banks that surface 3D Secure in unusual ways.

These aren’t blockers — they’re build items in front of me. The cart processes real orders today, ships real furniture from real suppliers, and reconciles back through Stripe with proper webhook plumbing. The roadmap is what makes year three of the build better than year one.

When This Build Is the Right Answer

Five conditions, all of them true at once. Less than this, and you should be on Shopify.

  1. The merchant has a clear technical reason platform-bound stores can’t accommodate. Content-commerce blend where the brand experience matters more than the catalogue depth (luxury, editorial, educational). Unusual catalogue structures that themes can’t fit. Deep customisation beyond what Liquid templates support. A genuinely headless content strategy where the storefront is one of several surfaces drawing from a shared CMS.
  2. The catalogue is relatively stable. A custom build amortises across years of operation. If the product mix changes weekly and new features need shipping monthly, the maintenance burden swamps the build benefit.
  3. The merchant accepts the app ecosystem trade. No Klaviyo flows in fifteen minutes. No reviews app in twenty minutes. Every integration is custom code or skipped entirely. Some merchants find this liberating; most find it expensive.
  4. The business case justifies ongoing engineering. Either the merchant has technical capacity in-house, or they’re paying someone (me, in Indoor Luxury’s case) to maintain the cart on an ongoing basis. The savings vs. Shopify need to clear the maintenance bill.
  5. The merchant understands they’re trading a known platform for a bespoke system. Shopify is the default for ecommerce because it works. Choosing not to use it requires conviction about why, not just resistance to monthly fees.

For Indoor Luxury, all five are true. For nearly every client I work with, they aren’t.

When This Build Is Wrong

If you don’t fit the five conditions above, there are two better paths than full custom.

Shopify with a great theme. For most merchants, Shopify is the default for good reasons. Hours-to-launch instead of months. Battle-tested checkout. Mature app ecosystem. The EKM vs Shopify comparison piece covers the platform decision for those still weighing it, and the migration guide covers execution.

Headless Shopify with an Astro frontend. The realistic middle path for merchants who want Astro’s performance and customisation flexibility without rebuilding commerce from scratch. Shopify handles the back-end (catalogue, checkout, payments, fulfilment, apps) and Astro handles the public storefront. Most merchants who think they want fully custom actually want this hybrid.

Lessons That Shape the Next Iteration

What the first version of TonyCart taught me, that the next version embeds from the start.

  • The pattern library was the right call from day one. The Empire portfolio plan meant the cart had to be reusable from the start, and that paid off the moment the second property started drawing on the same components. Every improvement compounds. The temptation to inline a cart “for one site, extract later” is always a trap — that extraction never happens cleanly.
  • Email capture early earns the abandoned-cart story. The current cart captures email at checkout step one. The next iteration uses that capture point as the trigger for cart-recovery emails when the customer doesn’t complete. Same database write, much higher commercial value.
  • Postcode shipping logic deserves unit tests. The shipping rules are exactly the kind of thing that quietly breaks when you change them — overlapping prefixes, district vs. area precision, the Highland Aberdeenshire trap that the Parcelify piece covers in depth. The next pass adds proper test coverage so the regression risk goes from “I’ll know when a customer complains” to “I’ll know at deploy time.”
  • The Payment Intents choice was right for luxury, but Checkout Sessions would have been right for the others. The decision to keep the customer on-site cost more engineering time than I’d budgeted for, and the conversion case for it is real on luxury furniture where trust signals matter. For an Empire property where the catalogue is lower-AOV and conversion is more transactional, Checkout Sessions ships faster and converts identically. The pattern library can support both — and that flexibility is the next architectural pass.

The Honest Bottom Line

I built TonyCart because the combination of an owned property, simple commerce mechanics, and a real reason to walk the frontier made it possible. The build works, the site sells real furniture, the code is reusable across the Empire portfolio, and the work taught me things about ecommerce architecture that now inform every platform conversation I have with clients.

The lesson isn’t “you should build this.” For most merchants, Shopify is right, and a custom Astro cart is the wrong answer. But for the rare merchant who fits the five conditions — and for me, running a portfolio where the cart powers seven properties — the build is a strategic asset, not an indulgence. It earns the right to advise on platforms by having built the alternative. It compounds across the portfolio. It removes the growth tax that every platform-bound store pays.

If you’ve read this far and you’re convinced you fit the rare profile, get in touch. I’ll be honest about whether the build is right for you, including frequently recommending Shopify or the hybrid headless path instead. The Indoor Luxury build makes me a better advisor, not a custom-build salesperson.

Companion reading: the EKM vs Shopify honest comparison covers the platform decision most merchants are making, and the UK Shopify shipping guide covers the postcode-zone work that powered the TonyCart shipping engine — the same knowledge, two platforms, very different implementation costs.

Tony Cooper

Tony Cooper

Founder

Put My Crackerjack Digital Marketing Skills To Work On Your Next Website Design Project!

Get Started

100% satisfaction guarantee. Not happy within 30 days? Full refund, no questions. Details

Call: 01952 407599