Order Summary
The checkout and cart order read-out. One composable card that stacks line items, a promo-code band and a totals breakdown, with every price on tabular-nums so the column never jitters.
Installation#
Usage#
import {
OrderSummary,
OrderSummaryHeader,
OrderSummaryTitle,
OrderSummaryItems,
OrderSummaryItem,
OrderSummaryItemThumbnail,
OrderSummaryItemContent,
OrderSummaryItemName,
OrderSummaryItemOptions,
OrderSummaryItemPrice,
OrderSummaryPromo,
OrderSummaryTotals,
OrderSummaryRow,
OrderSummaryTotal,
OrderSummaryFooter,
} from "@/components/ui/order-summary"
export function Example() {
return (
<OrderSummary>
<OrderSummaryItems>
<OrderSummaryItem>
<OrderSummaryItemThumbnail count={2}>
<img src="/sneakers.jpg" alt="Retro Sneakers" />
</OrderSummaryItemThumbnail>
<OrderSummaryItemContent>
<OrderSummaryItemName>Retro Sneakers</OrderSummaryItemName>
<OrderSummaryItemOptions>Size 10 · Red</OrderSummaryItemOptions>
</OrderSummaryItemContent>
<OrderSummaryItemPrice>$64.00</OrderSummaryItemPrice>
</OrderSummaryItem>
</OrderSummaryItems>
<OrderSummaryTotals>
<OrderSummaryRow label="Subtotal">$64.00</OrderSummaryRow>
<OrderSummaryTotal label="Total">$64.00</OrderSummaryTotal>
</OrderSummaryTotals>
</OrderSummary>
)
}Anatomy#
OrderSummary is a multi-part component: one tv recipe whose slots flow to every named part through React Context. Compose only the bands you need, in any order. There are no internal dividers or boxing: the bands and the line items are simply set apart by whitespace (the house minimal style), so the summary reads clean on any surface. The card variant adds an outer border and insets the content with padding; plain sits flush.
<OrderSummary> {/* the card shell */}
<OrderSummaryHeader> {/* title + item-count Badge */}
<OrderSummaryTitle />
</OrderSummaryHeader>
<OrderSummaryItems> {/* the <ul> of line items */}
<OrderSummaryItem> {/* one <li> row */}
<OrderSummaryItemThumbnail count={2} />
<OrderSummaryItemContent>
<OrderSummaryItemName />
<OrderSummaryItemOptions />
</OrderSummaryItemContent>
<OrderSummaryItemPrice />
</OrderSummaryItem>
</OrderSummaryItems>
<OrderSummaryPromo /> {/* slot for an InputGroup */}
<OrderSummaryTotals> {/* the <dl> breakdown */}
<OrderSummaryRow label="Subtotal">…</OrderSummaryRow>
<OrderSummaryTotal label="Total">…</OrderSummaryTotal>
</OrderSummaryTotals>
<OrderSummaryFooter /> {/* slot for the checkout CTA */}
</OrderSummary>Line items#
Each OrderSummaryItem is a <li> laying out a thumbnail, the name + options, and the line price. The thumbnail holds an <img> (auto object-cover with the DS image-outline) or a Phosphor glyph, and its count prop drops a quantity pill on the corner. The name truncates rather than shoving the price off the row, and the price is tabular-nums so the column stays aligned.
Shopping cart#
Compose OrderSummaryItemActions after the price to turn the read-out into a shopping cart. It holds OrderSummaryItemQuantity (the canonical NumberInput in its compact inline − value + form) and OrderSummaryItemRemove (the canonical Button in its quiet destructiveGhost trash form). Wire their onValueChange / onClick to your cart state; here the line prices and the subtotal recompute live. Removing a row sets leaving on its OrderSummaryItem so it collapses and drifts out before it's dropped from state (the rows below glide up, no jump); clearing the cart shows the empty state.
Totals & tones#
OrderSummaryTotals renders a semantic <dl>. Each OrderSummaryRow takes a label (the <dt>) and its value as children (the <dd>). The tone prop accents the value: discount greens a credit, muted softens a "Free" or "calculated later" placeholder. OrderSummaryTotal is the emphasized grand total, set apart from the rows by extra whitespace.
Promo code#
OrderSummaryPromo is a band you fill with the canonical InputGroup — a field joined to an Apply button in one seamless shell. Nothing is re-styled inline; the summary composes the real controls, so the promo row stays in lockstep with the input family.
The band imposes the big control tier on what you put in it (size="lg", 40px), the way Form does for its fields: a promo code is a real form control, not a table filter, so it matches the Checkout button under the totals instead of shrinking to the ambient density. Pass size for a different tier; a size on the control itself still wins.
<OrderSummaryPromo>
<InputGroup>
<InputRoot>
<InputField placeholder="Promo code" />
</InputRoot>
<Button variant="secondary">Apply</Button>
</InputGroup>
</OrderSummaryPromo>Variants & density#
The default card variant is the bordered checkout sidebar. plain drops the frame so the bands sit flush on whatever surface holds them — a page column or a Drawer. Density is Koala's spacing axis: comfortable (the default) suits a checkout page; compact tightens the padding and shrinks the thumbnails for a mini-cart. Set it per instance or once on a DensityProvider.
API reference#
OrderSummary
The card shell (a <section>). Props: variant ("card" | "plain", default "card"), density ("comfortable" | "compact"), asChild, plus all <section> props. Density resolves prop > nearest DensityProvider > "comfortable".
OrderSummaryHeader / OrderSummaryTitle
The title band (a justify-between row) and its title. OrderSummaryTitle takes asChild to render a real heading (<h2>/<h3>).
OrderSummaryItems / OrderSummaryItem
The line-item <ul> and each <li> row. Rows stack with whitespace (the density-scaled items gap), no dividers.
OrderSummaryItemThumbnail
The product tile. Holds an <img> (auto object-cover + image outline), a Phosphor glyph, or nothing. Prop count renders a quantity pill on the corner.
OrderSummaryItemContent / Name / Options / Price
The content column (name truncates), the muted options line, and the trailing line price (tabular-nums).
OrderSummaryPromo
A band; fill it with an InputGroup (promo field + Apply button). Takes size (sm · md · lg, default lg), the control height it imposes on its children.
OrderSummaryTotals / OrderSummaryRow / OrderSummaryTotal
The totals <dl>. OrderSummaryRow takes label (<dt>), its value as children (<dd>), and tone ("default" | "discount" | "muted"). OrderSummaryTotal is the emphasized grand total.
OrderSummaryFooter
A band for the checkout Button and a trust note.