Commerce & billing
Products, carts, prices, plans and invoices.
Product & price #
Product tiles and prices.
ProductCard #
Product tile.
Stoneware mug
In cart: 0
function Example() {
const inCart = signal(0);
return (
<Stack gap="0.5rem">
<ProductCard
title="Stoneware mug"
order={3}
price={24}
strike={32}
badge="Sale"
image="/images/mug.jpg"
imageAlt="Terracotta stoneware mug"
onAdd={() => inCart.set(inCart() + 1)}
/>
<Text muted>In cart: {inCart()}</Text>
</Stack>
);
}Slots: root badge body footer image media title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
price | number | yes | Price as a number. | |
title | string | yes | Product name. | |
addLabel | content | "Add" | Label for the add button (default "Add"). | |
badge | content | Badge over the image, e.g. "Sale". | ||
currency | string | ISO currency code for formatting. | ||
image | string | Product image URL. | ||
imageAlt | string | "" | Alt text for the product image (default: decorative). | |
onAdd | () => void | Shows an add-to-cart button; called when it is pressed. | ||
order | HeadingLevel | 4 | Heading level of the title, to fit the page outline. Default 4. | |
strike | number | Previous price, shown struck through. |
HeadingLevel
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;Price #
Formatted price with optional strike-through and period.
<Price amount={24} strike={32} period="month" />Slots: root amount period strike
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
amount | number | yes | Price as a number. | |
currency | string | "USD" | ISO currency code for formatting, e.g. USD. | |
period | string | Billing period after the price, e.g. month. | ||
strike | number | Previous price, shown struck through. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Cart #
Cart lines and order totals.
CartLine #
Cart row.
Stoneware mug
$24.00function Example() {
const quantity = signal(2);
const removed = signal(false);
return (
<Show
when={!removed()}
fallback={
<Button size="sm" variant="outline" onClick={() => removed.set(false)}>
Undo remove
</Button>
}
>
<CartLine
title="Stoneware mug"
price={24}
quantity={quantity()}
image="/images/mug.jpg"
onQuantityChange={quantity.set}
onRemove={() => removed.set(true)}
/>
</Show>
);
}Slots: root body image quantity remove title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
price | number | yes | Unit price as a number. | |
quantity | number | yes | Quantity in the cart. | |
title | string | yes | Product name. | |
currency | string | ISO currency code for formatting. | ||
image | string | Product thumbnail URL. | ||
onQuantityChange | (value: number) => void | Shows quantity buttons; called with the new quantity. | ||
onRemove | () => void | Shows a remove button; called when it is pressed. |
OrderSummary #
Subtotal / tax / total block.
<OrderSummary
lines={[
{ label: "Subtotal", value: "$48.00" },
{ label: "Shipping", value: "Free", muted: true },
{ label: "Tax", value: "$4.32" },
]}
total="$52.32"
/>Slots: root row total
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
lines | OrderSummaryLine[] | yes | Summary lines above the total. | |
total | content | yes | Total amount. | |
children | content | Content below the total, e.g. a checkout button. | ||
totalLabel | content | "Total" | Label for the total row (default "Total"). |
OrderSummaryLine
| Field | Type | Required | Description |
|---|---|---|---|
label | string | yes | Line label, e.g. "Shipping". |
value | string | yes | Line amount text. |
muted | boolean | De-emphasise the line. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Plans #
Pricing cards and feature comparisons.
PricingCard #
Pricing plan card with price, features and a call to action.
Pro
$20/per seat / month
For growing teams.
- Unlimited projects
- 10,000 build minutes
- Email support
<PricingCard
name="Pro"
price="$20"
period="per seat / month"
description="For growing teams."
features={["Unlimited projects", "10,000 build minutes", "Email support"]}
highlighted
action={<Button onClick={() => {}}>Start trial</Button>}
/>Slots: root action description features name period price
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | Plan name. | |
price | string | yes | Price text, e.g. $20. | |
action | content | Call to action at the bottom, e.g. a Button. | ||
description | string | Short pitch under the name. | ||
features | string[] | Features included in the plan. | ||
highlighted | boolean | Emphasise this plan (e.g. the recommended one). | ||
period | string | Billing period after the price, e.g. per month. |
FeatureList #
List of features with check icons.
- Unlimited projects
- Preview deploys
- SSO & audit log
<FeatureList items={["Unlimited projects", "Preview deploys", "SSO & audit log"]} />Slots: root icon item label
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
items | string[] | yes | Features, one per line. | |
icon | IconName | "check" | Icon before each feature. |
IconName — Material Design Icons path names used by <Icon />.
type IconName = | "check" | "x" | "plus" | "minus" | "search" | "user" | "users" | "settings" | "menu" | "home" | "heart" | "star" | "bell" | "mail" | "calendar" | "clock" | "edit" | "trash" | "copy" | "download" | "upload" | "link" | "external" | "info" | "warning" | "error" | "success" | "chevron-down" | "chevron-up" | "chevron-left" | "chevron-right" | "arrow-left" | "arrow-right" | "eye" | "eye-off" | "eye-outline" | "lock" | "unlock" | "filter" | "more" | "close" | "spinner" | "sun" | "moon" | "play" | "pause" | "refresh" | "share" | "image" | "file" | "folder" | "zap" | "phone" | "git" | "code";FeatureCompare #
Plan comparison grid (ARIA table over CSS grid rows).
<FeatureCompare
plans={["Free", "Pro", "Enterprise"]}
rows={[
{ feature: "Projects", values: ["3", "Unlimited", "Unlimited"] },
{ feature: "Preview deploys", values: [true, true, true] },
{ feature: "SSO", values: [false, false, true] },
]}
/>Slots: root cell head row
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
plans | string[] | yes | Plan names (the columns). | |
rows | FeatureCompareRow[] | yes | Features (the rows). | |
featureLabel | content | "Feature" | Header of the first column (default "Feature"). |
FeatureCompareRow
| Field | Type | Required | Description |
|---|---|---|---|
feature | string | yes | Feature name. |
values | (string | boolean)[] | yes | One cell per plan: true / false for a check or cross, or text. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Billing #
Payment methods and invoices.
CreditCardPreview #
Masked payment card preview.
•••• •••• •••• 4242
<CreditCardPreview brand="Visa" last4="4242" exp="08/29" name="Ada Lovelace" />Slots: root bottom brand number top
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
last4 | string | yes | Last four digits. | |
brand | string | "Card" | Card network, e.g. Visa. | |
exp | string | Expiry, e.g. 08/29. | ||
name | string | "Cardholder" | Cardholder name. |
InvoiceRow #
Invoice row with number, date, amount and status.
<InvoiceRow
id="INV-2026-014"
date="Sep 1, 2026"
amount="$49.00"
status="paid"
onClick={() => {}}
/>Slots: root amount date number status
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
amount | string | yes | Amount text, e.g. $49.00. | |
date | string | yes | Invoice date text. | |
id | string | yes | Invoice number shown in the row. Also forwarded as the element id. | |
status | InvoiceStatus | yes | Payment state; sets the badge. | |
onClick | () => void | Makes the row a button, e.g. to open the invoice. |
InvoiceStatus
type InvoiceStatus = "paid" | "open" | "void" | "past_due";Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.