---
title: Cart Summary
description: The order totals — subtotal, discount, shipping, tax and an emphasized grand total below the rule.
category: Display
related:
  - /components/cart-list
  - /components/price
  - /components/data-list
  - /components/divider
visibility: guest
draft: false
---

# Cart Summary

The panel beside the cart. It takes the amounts your application already computed and gives
them a shape: a named section, a column of parts, a rule, and the total below it.

:::preview{title="An order summary"}
<x-wirekit::cart-summary
    :subtotal="12400"
    :discount="500"
    :shipping="450"
    :tax="2356"
    :total="14706"
    :minor-units="true"
    currency="EUR"
>
    <x-slot:actions>
        <x-wirekit::button>Checkout</x-wirekit::button>
    </x-slot:actions>
</x-wirekit::cart-summary>
:::

## It computes nothing, and that is the design

::: warning
Every amount is a prop. The component never adds a subtotal, never applies a tax rate, and
never derives a total from the rows above it.

Totals are where tax rules, tiered pricing, gift cards and currency rounding live. A component
that guessed at them would be wrong in every second shop — and taking a wrong assumption back
out costs more than never having shipped it. This is the same rule
[cart-list](/components/cart-list) follows for a line total.
:::

## Rows you did not pass are not there

Pass `subtotal` alone and you get one row. Each of `discount`, `shipping` and `tax` appears
only when you give it a value, so a shop with no tax line does not have to hide one. An empty
string, the value of a field nobody has filled yet, counts as no value.

:::preview{title="A summary with nothing but a subtotal and a total"}
<x-wirekit::cart-summary :subtotal="12400" :total="12400" :minor-units="true" currency="EUR" />
:::

## Shipping

Shipping is the row that most often is not a number yet. Two ways to say so:

- **`:shipping="0"`** renders the word rather than a zero amount, because "0.00" beside
  Shipping reads as a price and "Free" reads as the offer it is.
- **`shipping-note`** replaces the amount with a phrase — "Calculated at checkout", "Free over
  €50". It wins over `shipping` when both are given: a shop that passes both means the words.

"Free" is only said for a shipping cost that is a number and zero. A word in `shipping`, such as
`"on request"`, goes on to [`price`](/components/price) like any amount that is no number: it is
reported while you develop and left out in production, so the phrase belongs in `shipping-note`.
An empty string, such as a shipping method nobody has chosen yet, hides the row as `null` does.

:::preview{title="Free shipping, and shipping that is not known yet"}
<x-wirekit::stack gap="lg">
    <x-wirekit::cart-summary :subtotal="12400" :shipping="0" :total="12400" :minor-units="true" currency="EUR" />
    <x-wirekit::cart-summary :subtotal="12400" shipping-note="Calculated at checkout" :total="12400" :minor-units="true" currency="EUR" />
</x-wirekit::stack>
:::

## Discounts

A discount renders as a reduction. The minus sign sits beside the formatted amount rather than
inside it — passing a negative number to [price](/components/price) would make the sign a
currency-formatting decision, which differs by locale.

## Your own rows

Anything in the default slot is appended to the parts, above the rule. Use
[data-list.item](/components/data-list), which is what the component itself uses, and write the
amount with [price](/components/price) in the same `currency`, `minor-units` and `locale`, so your
row reads like the rows the component draws.

:::preview{title="A gift card and a second tax rate"}
<x-wirekit::cart-summary :subtotal="12400" :tax="2356" :total="13756" :minor-units="true" currency="EUR">
    <x-wirekit::data-list.item label="Gift card">&minus;<x-wirekit::price :amount="1000" :minor-units="true" currency="EUR" /></x-wirekit::data-list.item>
    <x-wirekit::data-list.item label="Reduced rate (7%)"><x-wirekit::price :amount="800" :minor-units="true" currency="EUR" /></x-wirekit::data-list.item>
</x-wirekit::cart-summary>
:::

## Beside the cart

The summary is a sibling of [cart-list](/components/cart-list), not a part of it — a cart page
is two columns, and on a phone it is one.

:::preview{title="A cart page"}
<x-wirekit::grid cols="1 lg:3" gap="lg">
    <div class="lg:col-span-2">
        <x-wirekit::cart-list>
            <x-wirekit::cart-item name="Brass desk lamp" :price="3900" :minor-units="true" currency="EUR" :quantity="1" :line-total="3900">Warm white</x-wirekit::cart-item>
            <x-wirekit::cart-item name="Walnut notebook" :price="1200" :minor-units="true" currency="EUR" :quantity="2" :line-total="2400">A5 · Dotted</x-wirekit::cart-item>
        </x-wirekit::cart-list>
    </div>
    <x-wirekit::cart-summary :subtotal="6300" :shipping="0" :tax="1197" :total="7497" :minor-units="true" currency="EUR">
        <x-slot:actions>
            <x-wirekit::button>Checkout</x-wirekit::button>
        </x-slot:actions>
    </x-wirekit::cart-summary>
</x-wirekit::grid>
:::

In a Livewire component the summary reads the same lines as the list. With the `Cart` component
from [Wiring It to Livewire](/components/cart-list#wiring-it-to-livewire), the view adds it beside
the list:

```blade
{{-- resources/views/livewire/cart.blade.php, beside the cart-list --}}
@php
    // 1. The subtotal is the sum of the line totals the list shows.
    $subtotal = collect($lines)->sum(fn (array $line) => $line['price'] * (int) $line['quantity']);

    // 2. Shipping and tax are your shop's rules. This one charges a flat rate and shows no tax.
    $shipping = 450;
@endphp

<x-wirekit::cart-summary
    :subtotal="$subtotal"
    :shipping="$shipping"
    :total="$subtotal + $shipping"
    :minor-units="true"
    currency="EUR"
>
    <x-slot:actions>
        <x-wirekit::button href="/checkout">Checkout</x-wirekit::button>
    </x-slot:actions>
</x-wirekit::cart-summary>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `subtotal` | `int\|float\|null` | `null` | The parts before shipping and tax. |
| `discount` | `int\|float\|null` | `null` | A reduction, rendered with a minus sign. |
| `shipping` | `int\|float\|null` | `null` | Shipping cost. `0` renders as "Free"; a value that is no number is refused like a `price` amount, and an empty string hides the row. |
| `shippingNote` | `string\|null` | `null` | A phrase instead of an amount. Wins over `shipping`. |
| `tax` | `int\|float\|null` | `null` | Tax. |
| `total` | `int\|float\|null` | `null` | The grand total, below the rule. |
| `currency` | `string` | config | Currency code, forwarded to `price`. |
| `minorUnits` | `bool` | `false` | Treat the amounts as minor units (cents). |
| `locale` | `string\|null` | `null` | Locale every amount is written in, forwarded to `price`. Unset, the application's locale. |
| `label` | `string` | `Order summary` | Accessible name and heading. |
| `level` | `1`–`6` | `2` | Heading level. The outline is the page's, not this component's — a summary under an existing `<h2>` wants `3`. Clamped rather than trusted: an invalid value falls back to `2`, because `<h9>` is not a heading to any reader and the failure is silent. |
| `scope` | `string\|null` | `null` | Theme scope. |

## Slots

| Slot | Description |
|------|-------------|
| default | Extra rows, appended to the parts above the rule. |
| `actions` | The control that leaves the cart — a checkout button, a payment shortcut. |

## Accessibility

- The panel is a `<section>` carrying its own name, so a reader can jump straight to the
  totals instead of arrowing through every cart line to reach them.
- The rows are a real `<dl>` by way of [data-list](/components/data-list), so each amount is
  announced with the label it belongs to rather than as a loose run of numbers.
- The grand total is emphasized on **two** channels — size and weight. Color alone would be
  the color-only signal this library bans for prices, and it is the one row a reader looks for
  first.
- The minus sign on a discount is hidden from assistive tech and the amount is announced
  plainly. Read aloud, "minus ten euros" and "discount, ten euros" say the same thing; the
  glyph is there for the eye.

## Keyboard Interaction

The panel takes no focus and has no keyboard model of its own. Everything reachable inside it
comes from the `actions` slot and behaves the way that control behaves everywhere else.

## Further Reading

- [cart-list](/components/cart-list) — the lines this panel totals
- [data-list](/components/data-list) — the `summary` layout underneath, and its own recipe
- [price](/components/price) — currency formatting, minor units, compare-at
