React Forum
Blog & Guide Responses
Jan 11, 2023 · 04:00 PM

[Guide] Build an E-commerce Storefront - implementation notes

Started by Avery Chen
guidestorefrontcreateStateVaultuseVaultMirroruseVaultWritecheckout stepperderived totals

The Storefront guide is a strong example of the repo's philosophy applied to commerce: keep the file map calm, keep checkout motion in route state, and make the cart a global vault with local mirrors so the app stays consistent across routes. It also treats derived totals (subtotal/tax/total) as a debugging surface, not just a performance trick.

Read the guide

Did you actually mirror the cart into each route, or did you read the vault directly and accept slower renders? Where do you keep checkout stepper truth (route keys vs URL vs document), and how do you make step transitions reversible? How do you prevent cart totals from drifting when different panels write items/coupons? If you're doing "freshness by default" on product grids, what do you render as evidence that a grid is fresh vs cached?

Comments (16)

Back to latest
Marisol Vega
Jan 15, 2023 · 10:18 PM

The mirror approach was better than I expected.

Reading the vault directly made the cart badge feel laggy whenever a route rerendered under load.

Dmitri Kline
Jan 22, 2023 · 08:03 AM

We treated checkout step as pure route truth and made it self-correct in render.

If step=payment but the cart is empty, we write step=cart and jump. It's blunt but it avoids dead ends.

Keira Santos
Feb 03, 2023 · 05:20 PM

Counterpoint: a global cart vault can become "one giant store" very quickly.

Once you add shipping address, promo rules, and inventory flags, the cart starts owning half the app.

Avery Chen
Feb 06, 2023 · 10:44 AM

Agree. We kept the vault surface intentionally small and stored only keys that multiple routes needed.

Everything else lived in a checkout document keyed by session id, and we rendered that id as evidence.

Grace McCoy
Feb 18, 2023 · 02:30 AM

Storing derived totals saved us in support tickets.

Users would screenshot "total wrong" and we'd see subtotal=… tax=… and immediately know which lane was broken (item pricing vs tax calc).

Nina Kapoor
Mar 01, 2023 · 12:55 PM

We made totals derivation a single helper and called it in the mutation path:

ts
function applyCartMutation(vault: any, nextItems: any[]) {
  vault.write('items', nextItems);
  const subtotal = nextItems.reduce((s: number, it: any) => s + it.price * it.qty, 0);
  const tax = Math.round(subtotal * 0.08);
  vault.write('subtotal', subtotal);
  vault.write('tax', tax);
  vault.write('total', subtotal + tax);
  vault.write('lastIntent', { type: 'reprice', at: Date.now() });
}

Derivation in one place made it hard to forget updates when coupons changed.

Jasper Nguyen
Mar 15, 2023 · 06:05 PM

On product grid freshness: we rendered a data-freshness attribute and an ISO timestamp of last read in the grid header.

People stopped reporting "stale results" once they could see when the grid last refreshed.

Camille Duarte
Apr 04, 2023 · 06:20 AM

A misunderstanding I had at first: I assumed checkout step should be in the URL path.

Route state as stepper truth is nicer because you can keep the map calm and still express a lot of motion.

Owen Price
Apr 19, 2023 · 10:11 PM

Mirrors helped us avoid tearing when multiple parts of the UI needed cart count.

If each panel reads vault and derives count, you can get subtle inconsistencies on fast updates.

Lina Ortega
May 07, 2023 · 01:49 PM

We rendered data-currency and data-cart-items on the shell and it made pricing bugs much easier to reproduce.

Commerce bugs are often configuration bugs, not component bugs.

Benji Rios
May 30, 2023 · 02:41 AM

Checkout reversibility mattered more than I expected.

We kept a lastStepIntent in route state so back/forward + stepper clicks were explainable.

Amina Farouk
Jun 21, 2023 · 11:00 AM

Docs tie-in: this guide is basically "state is a contract" applied to commerce.

Related: State Management (Best Practices) and Routing and Navigation.

Tomas Rivera
Aug 03, 2023 · 09:14 AM

We treated coupons as intent objects rather than strings so we could log why a coupon was rejected.

It fits the guide posture: if there's a rule, it should be visible.

Priya Liao
Oct 07, 2023 · 06:26 PM

Counterpoint: storing derived totals can hide pricing bugs if the source items are wrong but totals look consistent.

We also rendered a derived "pricing evidence" string that included item ids + qty + computed line totals.

Hana Saito
Nov 22, 2023 · 06:58 AM

The guide convinced me that cart consistency is a UX feature.

Even if the totals are "wrong" for a moment, a consistent story is less confusing than flicker.

Evan Brooks
Feb 09, 2024 · 08:32 PM

Has anyone tried using route session slugs for checkout (like the calendar guide does for editing)?

We did and it made it easier to resume checkout, but it added a lot of session cleanup work.

Nina Kapoor
Feb 13, 2024 · 12:10 PM

We did session slugs too and ended up treating them like a document identity (expire + fork).

Once you render the session id as evidence, the complexity is at least understandable.

Salma Qureshi
Apr 04, 2024 · 04:04 AM

The calm map + busy motion approach is the part I'd keep even if you change everything else.

It makes commerce flows feel like an app, not like a series of page loads.