Checkout
Cart, address, shipping, payment and review, as one guided flow.
SourcePackage @weft-js/warp
Preview
tsx
import { state } from '@weft-js/core'import { Checkout, PayFrame, Toaster } from '@weft-js/warp'import type { PlaceOrderResult } from '@weft-js/warp'export function Shop() { const cart = state(SHOP_CART) const place = async (): Promise<PlaceOrderResult> => { await wait(900) return { order: { id: 'T-2041', stage: 'placed' } } } return ( <> <Checkout cart={cart} shipping={SHOP_SHIPPING} countries={['US', 'GB', 'DE']} taxRate={0.08} coupon={code => (code === 'WEFT10' ? { code, percentOff: 10 } : undefined)} payment={<PAYMENT_FRAME />} onPlaceOrder={place} onContinueShopping={() => (cart.value = SHOP_CART)} /> <Toaster /> </> )}const SHOP_CART = [ { id: 'key', name: 'Security key, USB-C', variant: 'Pack of 2', price: 58, quantity: 1 }, { id: 'hoodie', name: 'weft hoodie', variant: 'Graphite · M', price: 64, quantity: 1 }, { id: 'stickers', name: 'Sticker sheet', variant: 'Deploy on Friday', price: 6, quantity: 2 },]const SHOP_SHIPPING = [{ id: 'std', name: 'Standard', eta: 'Arrives Thu 6 Nov', price: 0 }, { id: 'exp', name: 'Express', eta: 'Arrives Mon 3 Nov', price: 12 }]const wait = (ms: number) => new Promise<void>(done => { setTimeout(done, ms) })const providerFields = (el: HTMLElement) => { const fields = el.ownerDocument.createElement('p') fields.textContent = 'Your payment provider mounts its fields here.' el.append(fields) return { destroy: () => fields.remove() }}const PAYMENT_FRAME = () => <PayFrame title="Card fields from your payment provider" testMode ready mount={providerFields} />Installation
npm install @weft-js/warpUsage
tsx
import { Checkout } from '@weft-js/warp'Accessibility
- This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and warp's parts keep their roles and labels.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
cart | State<CartLine[]> | — | The lines, as a state, so quantity changes flow through the totals. |
shipping | ShippingOption[] | — | The delivery choices. |
countries | string[] | — | Country codes for the address form. |
defaultCountry | string | 'US' | The country selected at the start. |
addressProvider | AddressProvider | — | Address lookup, if you have one. |
taxRate | number | 0 | A flat rate, such as 0.08. |
currency | string | — | The ISO 4217 code prices are shown in, such as EUR. |
locale | string | — | The language and region used to format values. |
payment | unknown | — | The payment step's body, usually a PayFrame. |
wallet | { label: string; onClick: () => void; } | — | A saved wallet's button above the card form. |
coupon | ((code: string) => Coupon | Promise<Coupon | undefined> | undefined) | — | Checks a code. Resolve undefined when it is not valid. |
idempotencyKey | string | — | Your key for this attempt, sent again on a retry. |
signInHref | string | — | Shows a sign-in link at the start. |
| Event | Type | Default | Description |
|---|---|---|---|
onPlaceOrder | (order: OrderDraft) => Promise<PlaceOrderResult> | — | Places the order. Return the order, or a typed error that sends people to the right step. |
onStepChange | ((step: CheckoutStep) => void) | — | Called on every step change. |
onContinueShopping | (() => void) | — | The button on the final step. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="checkout"] [data-part="trigger"]
triggerstep-titlenavlayoutstepstep-bodycolssr-onlyradiodividersummarythumbpricetotal