Carts stored only in React useState or non-persisted Zustand evaporate on refresh, tab close, and session timeout. Shoppers who added three items, got distracted, and returned thirty minutes later see an empty cart and abandon. Baymard Institute pegs abandonment around 70%, and volatile cart state compounds every other UX issue in this bundle. Lost cart data is lost revenue, and it disproportionately hurts mobile shoppers whose browsers aggressively reclaim memory.
Critical because a cart that empties on refresh directly causes purchase abandonment and revenue loss on every session.
Write cart state to localStorage on every mutation and hydrate from it on mount in src/hooks/useCart.ts. For logged-in users, sync to a persistent backend through src/app/api/cart/route.ts so the cart survives device switches. Do not gate persistence behind if (user) with no anonymous fallback.
const [cart, setCart] = useState(() => JSON.parse(localStorage.getItem('cart') ?? '[]'))
useEffect(() => { localStorage.setItem('cart', JSON.stringify(cart)) }, [cart])
ID: ecommerce-cart-ux.cart-management.cart-persistence
Severity: critical
What to look for: Enumerate all cart state management locations in the codebase. Count every file that reads or writes cart data (e.g., context providers, hooks, store files, API routes). For each location, classify the storage mechanism as one of: (a) in-memory only (useState/useReducer with no persistence), (b) localStorage/sessionStorage, (c) cookies, (d) database/API, (e) hybrid. Quote the specific storage calls found (e.g., localStorage.setItem('cart', ...) in src/hooks/useCart.ts line 12).
Pass criteria: At least 1 persistent storage mechanism (localStorage, cookies, or database) is used for cart data. Code at the app initialization path (e.g., src/app/layout.tsx, src/providers/CartProvider.tsx, or equivalent) reads cart state from persistent storage on page load. Report even on pass: "Found X cart state locations using Y storage mechanism(s). Cart is loaded from persistent storage in Z."
Fail criteria: Cart is stored only in React useState(), Zustand without persist middleware, or other in-memory-only mechanisms. No localStorage, cookie, or database read is found on app initialization. A loading-state-only pattern (fetches cart but never persists it) does not count as passing.
Do NOT pass when: A persistence layer exists but is wrapped in a condition that prevents it from running (e.g., if (user) with no fallback for anonymous users), leaving unauthenticated carts volatile.
Skip (N/A) when: The project has no shopping cart functionality (e.g., informational site, SaaS with no physical products).
Detail on fail: Example: "Cart state is stored only in React useState() in src/hooks/useCart.ts with no persistence layer. 0 of 3 cart state locations write to localStorage, cookies, or database. Items are lost on refresh."
Cross-reference: For database-backed cart sync across devices, see the cross-device-sync check below. For cookie security flags on cart cookies, the Security Headers audit covers Secure/HttpOnly/SameSite configuration. For state management patterns, the Error Resilience audit covers data persistence and recovery.
Remediation: Add persistence in your cart hook or provider, typically at src/hooks/useCart.ts or src/providers/CartProvider.tsx:
// src/hooks/useCart.ts — localStorage fallback for anonymous users
import { useEffect, useState } from 'react'
export function useCart() {
const [cart, setCart] = useState(() => {
const stored = localStorage.getItem('cart')
return stored ? JSON.parse(stored) : []
})
useEffect(() => {
localStorage.setItem('cart', JSON.stringify(cart))
}, [cart])
return [cart, setCart]
}
For authenticated users, sync with your backend at src/app/api/cart/route.ts:
async function loadCart() {
const response = await fetch('/api/cart')
const data = await response.json()
setCart(data.items)
}