# Checkout Flow

Bag, details and receipt on one card, with editable addresses, saved cards, promo codes and a free-delivery meter; and a pay button that closes into a spinner and confirms with a check.

- Page and source: https://uibrik.com/components/checkout-flow
- Install (Pro, needs a license key, see https://uibrik.com/docs/pro): `npx shadcn@latest add @uibrik-pro/checkout-flow`
- Packages: none beyond React
- Import: `import { CheckoutFlow } from "@/components/uibrik/checkout-flow/checkout-flow"`

Where to use the Checkout Flow React component:

- **Online stores**: The whole checkout stays on one card, so shoppers never lose sight of what they are paying for. Pairs with [Cart Flight](https://uibrik.com/components/cart-flight).
- **Mobile commerce**: Address, delivery and payment each fit on one line until tapped, which keeps a small screen short.
- **Subscriptions and add-ons**: Every total rolls as quantities or delivery change, so the effect of each choice on the price is clear. Pairs with [Pricing Calculator](https://uibrik.com/components/pricing-calculator).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| defaultStep | `"bag" \| "details"` | `"bag"` | Step shown on mount. |
| taxRate | `number` | `0.08` | Sales tax as a fraction of subtotal plus shipping. 0 hides the row. |
| currency | `"USD" \| "EUR" \| "GBP" \| "JPY" \| "INR"` | `"USD"` | ISO 4217 code used to format prices. |
| locale | `string` | `"en-US"` | Locale used to format prices. |
| freeShippingThreshold | `number` | `250` | Subtotal after discounts that makes delivery free, with a meter in the bag. 0 turns it off. |
| simulateDecline | `boolean` | `false` | Make the first simulated payment fail, to see the error state. |
| maxQuantity | `number` | `9` | Most of one item in the bag. |
| photoRadius | `number` | `12` | Corner radius of the item photos. 28 gives circles. |
| sectionIcons | `boolean` | `true` | Show an icon beside address, delivery and payment. |
| title | `string` | `"Bag"` | Heading over the items. |
| checkoutLabel | `string` | `"Checkout"` | Primary button on the bag step. The subtotal follows it. |
| payLabel | `string` | `"Pay"` | Primary button on the details step. The total follows it. |
| doneLabel | `string` | `"Done"` | Button on the receipt. |
| simulateDelay | `number` | `1400` | How long the simulated payment takes when there's no `onPay`. |
| defaultItems | `{ id: string; title: string; variant?: string; price: number; quantity: number; image: string; alt?: string }[]` | `SAMPLE_ITEMS` | Items on mount. The card keeps its own state from then on. |
| onItemsChange | `(items: CheckoutItem[]) => void` |  | Called after a quantity changes, or an item is removed or restored. |
| defaultAddresses | `{ id: string; label: string; name?: string; street: string; city: string; region?: string; postcode?: string; country?: string }[]` | `SAMPLE_ADDRESSES` | Saved addresses on mount. The first is picked. Shoppers can edit, add and delete them in place. |
| onAddressesChange | `(addresses: CheckoutAddress[]) => void` |  | Called after an address is added, edited or deleted. |
| defaultCards | `{ id: string; brand: CardBrand; last4: string; expiry?: string }[]` | `SAMPLE_CARDS` | Saved cards on mount. Shoppers can add a card (number, expiry and CVC in one field) or remove one. |
| onCardsChange | `(cards: CheckoutCard[]) => void` |  | Called after a card is added or removed. Only the brand, last four and expiry are kept. |
| delivery | `{ id: string; label: string; detail?: string; price: number; eta?: string }[]` | `SAMPLE_DELIVERY` | Delivery speeds. `price` 0 shows Free and `eta` is printed on the receipt. |
| promoCodes | `Record<string, { percent?: number; amount?: number; freeShipping?: boolean }>` | `SAMPLE_PROMOS` | Codes the promo field accepts, matched without case. The samples are WELCOME10 and FREESHIP. Empty hides the field. |
| onPay | `(order: CheckoutOrder) => void \| Promise<unknown>` |  | Called with the items, address, delivery, card and totals when Pay is pressed. Return a promise to keep the spinner until it settles; a rejection's message is shown above the button. Without it, payment is simulated. |
| onDone | `(order: CheckoutOrder) => void` |  | Called with the placed order when Done is pressed on the receipt, after the card resets to the bag. |
| emptyText | `string` | `"Your bag is empty"` | Shown when the last item is removed. |
| className | `string` |  | Classes for the card. |
