obsidian/wiki/payloadcms/ecommerce-frontend.md
2026-05-15 15:22:27 +01:00

6.4 KiB

title aliases tags sources created updated
Ecommerce — Frontend React Utilities
ecommerce-react-hooks
payload-ecommerce-frontend
useCart
usePayments
payloadcms
ecommerce
react
hooks
stripe
cart
raw/ecommerce__frontend.md
2026-05-15 2026-05-15

Overview

@payloadcms/plugin-ecommerce/client/react exports a context provider + 6 focused hooks for managing cart, addresses, payments, and currency on the frontend. All hooks require EcommerceProvider to be mounted above them in the component tree.

EcommerceProvider

Wrap your app (or checkout subtree) with this provider:

import { EcommerceProvider } from '@payloadcms/plugin-ecommerce/client/react'
import { stripeAdapterClient } from '@payloadcms/plugin-ecommerce/payments/stripe'
import { USD, EUR } from '@payloadcms/plugin-ecommerce'

export const Providers = ({ children }) => (
  <EcommerceProvider
    enableVariants={true}
    currenciesConfig={{
      supportedCurrencies: [USD, EUR],
      defaultCurrency: 'USD',
    }}
    paymentMethods={[stripeAdapterClient]}
  >
    {children}
  </EcommerceProvider>
)

Key Props

Prop Default Notes
addressesSlug addresses Collection slug
cartsSlug carts Collection slug
customersSlug users Collection slug
currenciesConfig { supportedCurrencies, defaultCurrency }
paymentMethods Array of client-side payment adapters
enableVariants true Product variants support
syncLocalStorage true Persist cart ID across reloads; { key: 'cart' } for custom key
api.apiRoute /api Payload API base route
api.cartsFetchQuery Extra depth/select/populate for cart fetches
debug false Verbose console output

Hooks Reference

useCart

Manages cart state and item operations.

const { cart, addItem, removeItem, incrementItem, decrementItem, clearCart, isLoading } = useCart()
Property Type Notes
cart Cart | null Current cart state
addItem (item, quantity?) => void Add product/variant
removeItem (id) => void Remove by ID
incrementItem (id) => void +1 quantity
decrementItem (id) => void -1 quantity; removes at 0
clearCart () => void Empty the cart
isLoading boolean Any async op in flight

usePayments

Two-step checkout: initiatePaymentconfirmOrder.

const { initiatePayment, confirmOrder, paymentMethods, selectedPaymentMethod, isLoading } = usePayments()

Step 1 — initiate (cart verified, transaction created, no order yet):

const data = await initiatePayment('stripe', {
  additionalData: { customerEmail, billingAddress, shippingAddress },
})
// data.client_secret → complete payment with Stripe.js

Step 2 — confirm (after Stripe.js confirms payment on client):

const data = await confirmOrder('stripe', {
  additionalData: { paymentIntentID: paymentIntent.id, customerEmail },
})
// data.orderID → redirect to order page

Order is only created after confirmOrder. For off-site redirects (e.g. Stripe callback URL), route the relevant info back before calling confirmOrder.

useAddresses

const { addresses, createAddress, updateAddress, isLoading } = useAddresses()
Property Type
addresses Address[]
createAddress (data) => Promise<Address>
updateAddress (id, partial) => Promise<Address>

useCurrency

Formats prices (stored as integers) for display:

const { currency, setCurrency, formatPrice, currenciesConfig } = useCurrency()

<div>{formatPrice(amount)}</div>  // handles decimals per currency
  • All prices are stored as integers (avoids float precision issues)
  • formatPrice accounts for currency-specific decimal places

useEcommerceConfig

Access collection slugs and API settings for custom API calls:

const { cartsSlug, customersSlug, addressesSlug, api } = useEcommerceConfig()
const endpoint = `${api.apiRoute}/${cartsSlug}`

useEcommerce (unified hook)

Combines all hooks. Also exposes session management methods:

const {
  cart, addresses, config, isLoading,
  selectedPaymentMethod, clearSession,
  onLogin, onLogout, mergeCart, refreshCart,
} = useEcommerce()

Session Management

onLogin

Call after successful login to merge guest cart → user cart:

const response = await fetch('/api/users/login', { method: 'POST', body: ... })
if (response.ok) await onLogin()

Handles: fetch user data → merge guest items → transfer guest cart (if user has none) → clear guest secrets.

onLogout / clearSession

await fetch('/api/users/logout', { method: 'POST' })
onLogout() // alias for clearSession()

Clears: cart data, cart ID, cart secret, addresses, user state (memory + localStorage).

mergeCart

Manual cart merge (e.g. custom flows):

const mergedCart = await mergeCart('user-cart-id', 'guest-cart-id', 'guest-cart-secret')
// Source cart is deleted after merge; matching items have quantities combined

refreshCart

Force-sync cart from server:

const { refreshCart } = useEcommerce()
<button onClick={refreshCart}>Refresh</button>

Key Takeaways

  • Two-step checkout: initiatePayment verifies cart and creates a transaction; confirmOrder creates the actual order — never skip step 2.
  • Integer prices: All prices stored as integers; always use formatPrice() for display — never divide manually.
  • Guest → auth cart merge: Call onLogin() after login, not after — it handles the merge automatically.
  • syncLocalStorage: Cart ID persists across reloads by default; use { key: 'custom' } to avoid key collisions.
  • useEcommerce is the kitchen-sink hook: Use focused hooks (useCart, usePayments) for tree-shaking; useEcommerce for convenience when you need everything.
  • api.cartsFetchQuery.depth: Bump to 2+ if you need populated product/variant data in the cart response.

Sources