Skip to Content
BlocksCommerce journey

Commerce journey

ProductCollectionBlock, ProductDetailBlock, ShoppingCartBlock and CheckoutReviewBlock cover discovery through order review. Each block’s own page carries its full prop contract, states, verification record and limitations; this page is about the shared money contract and putting the four together.

import { ProductCollectionBlock, ProductDetailBlock, ShoppingCartBlock, CheckoutReviewBlock, type CommerceProduct, type CommerceCartItem, } from "@raydenui/ui/blocks";

A connected journey

const [step, setStep] = useState<"collection" | "detail" | "cart" | "review">("collection"); const [selected, setSelected] = useState<CommerceProduct | null>(null); const [items, setItems] = useState<CommerceCartItem[]>([]); return step === "collection" ? ( <ProductCollectionBlock products={products} currency="GBP" locale="en-GB" onSelectProduct={(product) => { setSelected(product); setStep("detail"); }} /> ) : step === "detail" && selected ? ( <ProductDetailBlock product={selected} onAddToCart={({ product, option, quantity }) => { setItems((current) => [ ...current, { ...product, id: `${product.id}-${option?.id ?? "default"}`, variant: option?.label, quantity }, ]); setStep("cart"); }} /> ) : step === "cart" ? ( <ShoppingCartBlock items={items} shipping={500} onQuantityChange={(id, quantity) => setItems((current) => current.map((item) => (item.id === id ? { ...item, quantity } : item))) } onRemoveItem={(id) => setItems((current) => current.filter((item) => item.id !== id))} onCheckout={() => setStep("review")} onContinueShopping={() => setStep("collection")} /> ) : ( <CheckoutReviewBlock items={items} deliveryAddress={["Alex Morgan", "24 Willow Lane", "London, N1 4AB"]} deliveryMethod="Standard delivery · 3–5 working days" paymentSummary="Visa ending in 4242" shipping={500} onEditCart={() => setStep("cart")} onConfirm={submitOrder} /> );

Cart-line ids must be unique per variant, not per product, which is why the example composes the product id with the chosen option id.

Amounts and validation

All prices, shipping, tax, and discounts are non-negative integers in the selected ISO currency’s minor unit. GBP 2400 displays as £24.00; JPY 2400 displays as ¥2,400. Every item in one block must use the same currency. The default is GBP with en-GB formatting; pass both for other markets. Quantities must be positive integers, product and line IDs unique, and stock a non-negative integer.

Totals are subtotal + shipping + tax - discount, floored at zero. Shipping and tax default to zero; provide amounts calculated by your application and use totalsNote to explain estimates, included tax, or final charges. Discounts should not exceed the order value. The host must validate prices, stock, addresses, shipping, and payment on the server before accepting an order.

Responsive and theme contracts

Layouts respond to their container, including a narrow sidebar inside a wide page. All four accept headingLevel (default h2), className, currency, and locale. Controls use native buttons, search, selects, and radio inputs with visible labels, keyboard focus, and 44px minimum heights. Surface, border, text, and action colors use Rayden’s semantic CSS variables. Product imagery remains the supplied image in every theme.

Last updated on