---
title: Cart
description: The cart system - state management, optimistic updates, server actions, and Shopify integration.
type: guide
---

# Cart



<CartBrowser />

> **Note:** The cart ID is stored in a `shopify_cartId` cookie with a 7-day expiry. Sessions longer than 7 days start a fresh cart.

The cart system spans the entire storefront, not just the cart page. A React Context provider wraps the app in `layout.tsx`, giving every component access to cart state. Mutations flow through server actions that call Shopify's GraphQL API, while optimistic updates keep the UI responsive during network round-trips.

## How it works

**Optimistic state, reconciled against the server.** `CartProvider` (`context.tsx`) exposes `useCart()` with `cart` (the last confirmed server state) and `cartWithPending` (that state merged with in-flight optimistic line items, used for rendering). It tracks an `originalCart` snapshot for rollback if a mutation fails; a failed mutation sets `lastError`, which a root-level notification adapter surfaces as a localized retry toast from any route, and starting another mutation clears the previous error. Shopify warnings stay inline in the overlay and full cart page instead, since they describe cart state the shopper may need to inspect rather than a failure to retry.

**Two debounce strategies, matched to how each action feels.** Add-to-cart uses a trailing-edge debounce at 400ms — rapid clicks accumulate into `pendingQuantity` and render as an optimistic line via `OptimisticProductInfo`, then a single request sends the accumulated quantity when the debounce fires. Quantity updates use a leading-edge debounce instead, so the first change fires immediately for instant feedback while subsequent changes within the window batch together; removals (quantity set to 0) skip debounce entirely. Each cart line also tracks a request ID that increments on every mutation, so a server response is only applied if its ID matches the latest — stale responses from an earlier, slower request are discarded rather than overwriting newer state.

> **Hard requirement:** every cart mutation must call `invalidateCartCache()` from `@/lib/cart/server`. Without it, the cached cart data goes stale and the UI shows outdated state after the next page navigation.

**Bundles nest as domain cart lines, not flattened rows.** The cart fragment reads both `CartLine` and `ComponentizableCartLine`, preserving each bundle parent's `lineComponents` as nested lines (`CartLine.components`), so fixed and transformed bundles render as one parent line listing its included products. The controls in `overlay-item.tsx` also honor Shopify's `canUpdateQuantity` and `canRemove` instructions rather than assuming every line is editable, so a fixed bundle's component lines can't be pulled out from under the parent. The fragment intentionally omits the deprecated cart tax and duty amounts (removed from the Storefront API in 2025-01) — Shopify finalizes those at checkout, so summaries show a taxes-and-shipping-at-checkout note instead of a misleading pre-checkout figure.

**The cart cookie's `SameSite` attribute adapts to the deployment.** It's derived from `VERCEL_ENV`: `Strict` in production, `Lax` on preview and every other environment. Preview deployments sit behind Vercel Authentication, whose SSO flow bounces the browser cross-site and redirects back — a `Strict` cookie would be withheld on that return navigation, dropping the cart id. `Lax` still rides top-level GET navigations back into the site (the SSO return, and the return from Shopify's checkout domain) while staying off cross-site subrequests, which is the right level for a cart identifier. The production custom domain is public, so it keeps `Strict`.

## Out of the box

* **Server actions** (`lib/cart/action.ts`, `"use server"`) — `addToCartAction()`, `updateCartQuantityAction()` (validates quantity 1–99), `removeFromCartAction()`, `syncCartLocaleAction()` (buyer identity for locale/currency switching), `updateCartNoteAction()`, `buyNowAction()` (checkout URL for a new item), and `prepareCheckoutAction()` (checkout URL for an existing cart).
* **Cart overlay** — a right-side `Sheet` on every viewport (`w-[calc(100%-2.5rem)] max-w-md`, the same width rule as the mobile menu and collection filter sidebar). `overlay-content.tsx` composes an empty state, a scrollable line-item list, and a summary with subtotal, checkout button, and a link to the full cart page. Each line item (`overlay-item.tsx`) reads from `cartWithPending` and renders image, title, variant options, bundle components, quantity controls, a remove button, and the line total.
* **Cart page** (`/cart`) — a 12-column grid on large screens (9 for items, 3 for a sticky summary), wrapped in `Suspense` with a skeleton fallback. Components live in `components/cart-page/`: `Header`, `CartItemsList` (reuses `OverlayItem`), `Summary`, and `Empty`. A `RelatedProductsSection` shows recommendations based on the first cart item; `Empty` replaces the whole grid when the cart has none.
* **Shopify integration** — `lib/shopify/operations/cart.ts` calls `cartLinesAdd`, `cartLinesUpdate`, and `cartLinesRemove`, each returning the full cart through `CartFields` so the server action can use the normalized response directly without a follow-up `getCart()` query. `lib/shopify/transforms/cart.ts` converts that GraphQL shape into the domain `Cart` type used throughout the app, isolating the rest of the codebase from Shopify's API structure.

## Common customizations

* **Custom bundle flows** — `addToCart()` accepts Shopify's optional `CartLineInput.parent` relationship, which the base implementation doesn't use but which is available as a foundation for app-specific customized-bundle UX beyond the fixed and transformed bundles rendered out of the box.
* **Cart lifetime** — the 7-day cookie expiry is a constant in the cart cookie helpers; extending or shortening session length is a one-line change if a store's checkout patterns call for it.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)