# Cart data (https://docs.salla.dev/twilight/react/data/cart)





Use this page when you build a mini-cart, a cart page or a checkout banner and need to know what the cart contains and what each field means.

## Read it [#read-it]

The cart lives in the browser: the cart id is stored there, so the server cannot load it. Ask the Salla SDK for the id, then fetch the cart with the [`cart` API module](https://docs.salla.dev/twilight/react/api/modules/cart).

```tsx title="app/components/cart/MiniCart.tsx"
import { useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { cart } from '@salla.sa/twilight-theme-engine/api/cart';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';

export function MiniCart() {
  const { format } = useMoney();
  const [cartId, setCartId] = useState<number | null>(null);

  useEffect(() => {
    window.salla?.cart.api.getCurrentCartId().then(setCartId);
  }, []);

  const { data } = useQuery({ ...cart.queries.detail(cartId!), enabled: !!cartId });

  if (!data) return null; // no cart yet, or still loading
  return (
    <p>
      {data.items.length} items, {format(data.total)}
    </p>
  );
}
```

The built-in [cart page](https://docs.salla.dev/twilight/react/routes/cart) does exactly this and shows a skeleton until the cart arrives. To follow changes live, listen to cart events ([React to cart events](https://docs.salla.dev/twilight/react/recipes/react-to-cart-events)) and invalidate the query.

## The `Cart` object [#the-cart-object]

Example (illustrative values):

<JsonExplorer data="cartExample" title="Cart object (illustrative values)" defaultOpen="2" />

The tables are generated from `Cart` in `@salla.sa/twilight-theme-engine/types`. Nested objects get their own tables: `items` (the lines), their options, offers and attachments, and `free_shipping_bar`. Example values are in the explorer above.

{/* types: ../../../../../theme-engine/src/types/index.ts#Cart depth=3 */}

| Field                  | Type                                                          | Description                                                                                                                                                                                                  |
| ---------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                   | `string`                                                      | Cart id. Twig: string — matches API string cart ID (SDK CartSummary incorrectly types this as number)                                                                                                        |
| `count`                | `number`                                                      | Number of items. Present in SDK CartSummary; not documented in Twig docblock                                                                                                                                 |
| `items`                | `CartItem[]`                                                  | The cart lines. An empty array means an empty cart.                                                                                                                                                          |
| `sub_total`            | `number`                                                      | Items total, the first row of the order summary.                                                                                                                                                             |
| `total`                | `string \| number`                                            | Final amount.                                                                                                                                                                                                |
| `discount`             | `number`                                                      | Discount amount.                                                                                                                                                                                             |
| `coupon?`              | `string \| null`                                              | Applied coupon code, or null.                                                                                                                                                                                |
| `tax_amount`           | `number`                                                      | Tax amount.                                                                                                                                                                                                  |
| `is_require_shipping?` | `boolean`                                                     | Whether anything in the cart needs shipping.                                                                                                                                                                 |
| `has_shipping`         | `boolean`                                                     | Whether a shipping cost applies now.                                                                                                                                                                         |
| `real_shipping_cost`   | `number`                                                      | The shipping cost.                                                                                                                                                                                           |
| `free_shipping_bar?`   | `FreeShippingBar \| null`                                     | Progress towards free shipping. Null or missing when the store does not offer it.                                                                                                                            |
| `options`              | `CartOption[]`                                                | Order-level options.                                                                                                                                                                                         |
| `options_total?`       | `number \| null`                                              | Total price of the order-level options.                                                                                                                                                                      |
| `total_discount?`      | `number \| null`                                              | Discount row of the order summary. Often 0, null or missing: test with `!!`.                                                                                                                                 |
| `should_refresh?`      | `boolean`                                                     | React-only runtime flag — triggers a client-side cart re-fetch after mutations                                                                                                                               |
| `gift?`                | `{ enabled: boolean; text?: string; type?: string; } \| null` | Gifting settings, or null. Twig exposes `gift` as a top-level page variable; the REST API sends it inside the cart object.                                                                                   |
| `loyalty?`             | `{ prize: { points: number; title: string; }; } \| null`      | The loyalty reward the customer can win, or null. Twig exposes `loyalty` as a top-level page variable, not nested inside `cart`; the REST API returns it inside the cart object, so the loader maps it here. |

#### `items` [#items]

| Field                     | Type                    | Description                                                                                                                                                          |
| ------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                      | `string`                | Line id. Use it to update or remove the line, not `product_id`. Twig docblock says `int`; API returns numeric string IDs — kept as string for safe attribute binding |
| `product_id`              | `number`                | Id of the product.                                                                                                                                                   |
| `product_name`            | `string`                | Product name.                                                                                                                                                        |
| `product_image`           | `string`                | Image URL (a plain string, not an image object).                                                                                                                     |
| `url`                     | `string`                | Product page URL.                                                                                                                                                    |
| `quantity`                | `number`                | Quantity in the cart.                                                                                                                                                |
| `max_quantity?`           | `number`                | Most the customer can buy.                                                                                                                                           |
| `type`                    | `string`                | Product type, such as "product" or "donating".                                                                                                                       |
| `price`                   | `number \| Money`       | Unit price the customer pays.                                                                                                                                        |
| `product_price`           | `number \| Money`       | Unit price of the product.                                                                                                                                           |
| `original_price?`         | `number \| Money`       | Price before a sale.                                                                                                                                                 |
| `total`                   | `number \| Money`       | Line total.                                                                                                                                                          |
| `total_special_price?`    | `number \| Money`       | Line total after tiered offers.                                                                                                                                      |
| `weight_label?`           | `string`                | Readable weight, such as "2 kg".                                                                                                                                     |
| `notes?`                  | `string`                | The customer's note.                                                                                                                                                 |
| `is_available`            | `boolean`               | False when the product is out of stock.                                                                                                                              |
| `is_hidden_quantity`      | `boolean`               | When true, show the quantity as text instead of a stepper.                                                                                                           |
| `is_on_sale?`             | `boolean`               | A sale price applies.                                                                                                                                                |
| `has_discount?`           | `boolean`               | A discount applies.                                                                                                                                                  |
| `can_add_note`            | `boolean`               | The product accepts a note.                                                                                                                                          |
| `can_upload_file`         | `boolean`               | The product accepts file attachments.                                                                                                                                |
| `offer?`                  | `CartItemOffer \| null` | A simple offer on this line, or null.                                                                                                                                |
| `detailed_offers?`        | `JsonObject[] \| null`  | Detailed offers, shown by the offers component.                                                                                                                      |
| `donation?`               | `JsonObject \| null`    | Donation data when `type` is "donating".                                                                                                                             |
| `attachments?`            | `CartItemAttachment[]`  | Files the customer uploaded.                                                                                                                                         |
| `options?`                | `CartItemOption[]`      | The options the customer picked.                                                                                                                                     |
| `has_pre_order_campaign?` | `boolean`               | Item has an active pre-order campaign. TODO: add to API response                                                                                                     |

#### `items.offer` [#itemsoffer]

| Field      | Type      | Description         |
| ---------- | --------- | ------------------- |
| `discount` | `number`  | Discount amount.    |
| `is_free`  | `boolean` | The item is free.   |
| `names?`   | `string`  | Offer name to show. |

#### `items.attachments` [#itemsattachments]

| Field        | Type     | Description                                                                        |
| ------------ | -------- | ---------------------------------------------------------------------------------- |
| `id`         | `number` | Attachment id.                                                                     |
| `url`        | `string` | File URL.                                                                          |
| `product_id` | `number` | Product the file belongs to.                                                       |
| `item_id`    | `number` | Cart line the file belongs to.                                                     |
| `name`       | `string` | File name. Twig docblock incorrectly types this as `int` — it is a filename string |

#### `items.options` [#itemsoptions]

| Field       | Type                                            | Description                        |
| ----------- | ----------------------------------------------- | ---------------------------------- |
| `id`        | `number`                                        | Option id.                         |
| `name?`     | `string`                                        | Option name.                       |
| `value?`    | `string`                                        | Chosen value.                      |
| `quantity?` | `number`                                        | Quantity, when the option has one. |
| `options?`  | `Record<string, string \| number \| boolean>[]` | Nested option details.             |

#### `free_shipping_bar` [#free_shipping_bar]

| Field               | Type      | Description                                                     |
| ------------------- | --------- | --------------------------------------------------------------- |
| `minimum_amount`    | `number`  | Cart value needed for free shipping.                            |
| `has_free_shipping` | `boolean` | True once the customer has reached it.                          |
| `percent`           | `number`  | Progress from 0 to 100, ready for a bar width.                  |
| `remaining`         | `number`  | Amount still missing. It is a plain number: format it yourself. |

#### `options` [#options]

Same fields as `items.options`.

#### `gift` [#gift]

| Field     | Type      | Description        |
| --------- | --------- | ------------------ |
| `enabled` | `boolean` | Gifting is on.     |
| `text?`   | `string`  | Gift message text. |
| `type?`   | `string`  | Gift type.         |

#### `loyalty` [#loyalty]

| Field   | Type                                 | Description |
| ------- | ------------------------------------ | ----------- |
| `prize` | `{ points: number; title: string; }` | The reward. |

#### `loyalty.prize` [#loyaltyprize]

| Field    | Type     | Description              |
| -------- | -------- | ------------------------ |
| `points` | `number` | Points the reward costs. |
| `title`  | `string` | Reward title.            |

{/* /types */}

All amounts are in the store's currency. Format them with [`useMoney`](https://docs.salla.dev/twilight/react/hooks/use-money), see [Money](https://docs.salla.dev/twilight/react/data/shared-types#money).

Use a line's `id` to update or remove it, not `product_id`. `CartItemOption` belongs to a line (a size, a colour, a text field); `CartOption` is an order-level option on `cart.options`, with the same shape. `loyalty` and `gift` are top-level in the API response; the engine copies them onto the cart object.

## Common tasks [#common-tasks]

Show the free-shipping progress:

```tsx
const bar = cart.free_shipping_bar;
if (bar && !bar.has_free_shipping) {
  return <progress max={100} value={bar.percent} />;
}
```

Read a price that may be a number or a `Money` object:

```tsx
import type { Money } from '@salla.sa/twilight-theme-engine/types';

const amount = (value: number | Money) => (typeof value === 'number' ? value : value.amount);
```

Change the cart with the Salla SDK, then let the query refresh:

```tsx
await window.salla?.cart.addItem(productId, 1);
await window.salla?.cart.updateItem({ id: item.id, quantity: 3 });
await window.salla?.cart.deleteItem(item.id);
```

Apply or remove a coupon with `useCoupon`:

```tsx
import { useCoupon } from '@salla.sa/twilight-theme-engine/hooks/useCoupon';

const { value, apply, remove, error, loading } = useCoupon();
await apply('SAVE10'); // resolves true on success, false on failure; `error` holds the message
// `value` starts as null on every page load: read the applied code from `cart.coupon`
```

## Share the cart with child components [#share-the-cart-with-child-components]

`CartContextProvider` and `useCartContext` carry a cart down your component tree so you do not pass it through every level.

```tsx title="app/components/cart/CartPage.tsx"
import { CartContextProvider, useCartContext } from '@salla.sa/twilight-theme-engine/contexts';

function Count() {
  const ctx = useCartContext(); // null outside a provider
  return <span>{ctx?.cart.count ?? 0}</span>;
}

// <CartContextProvider value={{ cart }}><Count /></CartContextProvider>
```

<Callout type="warn">
  The engine's own cart page does not mount this provider, and hook slots do not read it. Mount it yourself in your own cart page, and pass the cart to a slot with `context={{ cart }}`.
</Callout>

## Gotchas [#gotchas]

* `price`, `product_price`, `total` and the other line amounts can be numbers or `Money` objects. Do not assume one.
* Cart `id` is a string in the object, but the API call takes a number (`cart.queries.detail(cartId)`).
* After an SDK change, the cached cart is stale until you invalidate it. See the [`cart` API module](https://docs.salla.dev/twilight/react/api/modules/cart).
* `loyalty` is top-level in the API response and is placed on the cart object for you. `gift` already arrives inside the cart.
* `free_shipping_bar`, `coupon`, `total_discount` and the item `offer` are often absent. Guard each one.

## Next [#next]

* [Cart route](https://docs.salla.dev/twilight/react/routes/cart) and [cart slots](https://docs.salla.dev/twilight/react/slots/cart-and-checkout) for the cart page.
* [Shared types](https://docs.salla.dev/twilight/react/data/shared-types) for `Money`.
* [React to cart events](https://docs.salla.dev/twilight/react/recipes/react-to-cart-events)
