> ## Documentation Index
> Fetch the complete documentation index at: https://quickbutik.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Cart

> The stateless Cart API, the remembered cart, and the shared CartStore.

<Info>
  This page is the curated reference. The complete, generated type reference lives at [/kit/api/core](/kit/api/core) (the resource) and [/kit/api/react](/kit/api/react) (`CartStore`), regenerated from the published typings.
</Info>

The cart resource has two layers. The **stateless** methods map the Cart API one to one. The **remembered** methods add the one piece of state every storefront needs: which cart this visitor has, kept identically in the browser, a server component and a server action. On top of both sits the [`CartStore`](#cartstore), the observable cart the React hooks and the web components share.

The cart is server-owned and **prices are recomputed on every read**. Never cache cart figures beyond the current render. All money is in minor units. Shapes are on [Types](/kit/reference/types).

<Warning>
  `itemId` is the cart **line** id (a uuid, `item.id`), not the product id. `productId` is what you add; `item.id` is what you update and remove.
</Warning>

## Currency

A cart stores no currency of its own: it is **priced per request**. Every cart call (`create`, `get`, `addItem`, `updateItem`, `removeItem`, and so `current`, `ensure` and `add`) carries the client's currency as `?currency=`, so the same cart reads in SEK on one request and in EUR on the next. Switching currency never creates a new cart and never loses a line.

* `cart.currency` is what every amount in **that** response is in. Format with it, never a constant.
* `cart.presentment` says how that currency relates to the checkout: `{ mode, chargeCurrency, baseCurrency, exchangeRate }`. In `"display"` mode the amounts are converted for display and the checkout charges `chargeCurrency` (the shop's own). A missing `presentment` means `mode: "base"`.

```tsx theme={null}
{cart.presentment?.mode === "display" && (
  <p>Prices are shown in {cart.currency}. You will be charged in {cart.presentment.chargeCurrency}.</p>
)}
```

See [Currencies](/kit/concepts/currencies).

## Stateless methods

### cart.create

```ts theme={null}
shopkit.cart.create(options?: { storefrontId?: string; surface?: StorefrontSurface; signal?: AbortSignal }): Promise<Cart>
```

Creates a cart and hands it back **without remembering it**. Passing `storefrontId` creates a cart bound to that campaign storefront; the binding cannot change afterwards. Check it out with `checkout.start({ cartId })`.

### cart.get

```ts theme={null}
shopkit.cart.get(cartId: string, options?: { signal?: AbortSignal }): Promise<Cart | null>
```

`null` on 404 or 410: an expired cart is a normal answer.

### cart.addItem

```ts theme={null}
shopkit.cart.addItem(cartId: string, item: AddCartItemInput, options?: { signal?: AbortSignal }): Promise<Cart>
```

<ParamField body="productId" type="string | number" required>`"prod_123"` or `123`.</ParamField>
<ParamField body="variantId" type="number">The variant's numeric id. Required when the product has more than one variant.</ParamField>

<ParamField body="quantity" type="number" default="1" />

Adding the same product and variant again **increments the existing line**.

### cart.updateItem

```ts theme={null}
shopkit.cart.updateItem(cartId: string, itemId: string, quantity: number, options?): Promise<Cart>
```

Sets an absolute quantity on a line.

### cart.removeItem

```ts theme={null}
shopkit.cart.removeItem(cartId: string, itemId: string, options?): Promise<Cart>
```

### cart.delete

```ts theme={null}
shopkit.cart.delete(cartId: string, options?): Promise<void>
```

Deletes the cart and also forgets it locally when it is the remembered one.

## Remembered methods

### cart.currentId

```ts theme={null}
shopkit.cart.currentId(): Promise<string | null>
```

The remembered cart id. Makes no request.

### cart.current

```ts theme={null}
shopkit.cart.current(options?: { signal?: AbortSignal }): Promise<Cart | null>
```

Reads the remembered cart and **never creates one**. A remembered id that no longer resolves is forgotten and reported as `null`. Use it wherever a missing cart is a legitimate answer: a header badge, a server-rendered cart page, any page a crawler can reach.

### cart.ensure

```ts theme={null}
shopkit.cart.ensure(options?: CartCreateOptions): Promise<Cart>
```

Returns the remembered cart, or creates and remembers one. Needs writable storage: in a read-only context (a React Server Component) the cart is still created but not remembered.

### cart.add

```ts theme={null}
shopkit.cart.add(item: AddCartItemInput, options?: CartCreateOptions): Promise<Cart>
```

`ensure()` plus `addItem()`. It also recovers once from a stale cart: when the remembered id answers 404 or 410 it forgets it, creates a fresh cart and retries. Prefer it over `ensure()` followed by `addItem()`.

On a client bound to a campaign storefront the cart it creates is bound too. A per-call `storefrontId` here must equal the client's binding, or the call throws a `ShopkitConfigError` before any request. See [Campaign storefronts](/kit/concepts/campaign-storefronts).

### cart.clear

```ts theme={null}
shopkit.cart.clear(options?: { fallbackCartId?: string | null; signal?: AbortSignal }): Promise<void>
```

Deletes the remembered cart server-side and forgets it, so the next `add()` starts a fresh basket.

| Situation | Result |
| - | - |
| No remembered cart (and no `fallbackCartId`) | Nothing happens, no request |
| The delete answers 404 or 410 | The id is forgotten anyway; resolves normally |
| Any other failure (network, 5xx) | Throws, and the id is **kept** |

The remembered checkout session id and the shop's store id are left alone, so a thank-you page can still confirm an order already paid for. `fallbackCartId` names the cart to delete when storage remembers none (the cart on screen).

## CartStore

```ts theme={null}
import { CartStore } from "@quickbutik/kit/react"
```

The observable cart every React hook and every web component shares, so a header badge and a cart page never disagree. While anything is subscribed it watches the client's currency and **re-reads the same cart** when it changes; a read that a later switch overtook is dropped, so a slow answer can never land an older currency's cart. It is framework-free; React reads it with `useSyncExternalStore`. Without React, take the page's instance from the elements' `configure()` (`configure(...).cartStore`) or `Quickbutik.cart` on a script-tag page rather than constructing a second one.

```ts theme={null}
const store = new CartStore(client, initialCart)
```

<ParamField path="client" type="ShopkitClient" required />

<ParamField path="initialCart" type="Cart | null">A cart read on the server. The store then starts `ready`, so the first paint matches the server render.</ParamField>

### subscribe / getSnapshot

```ts theme={null}
store.subscribe(listener: () => void): () => void
store.getSnapshot(): CartSnapshot
```

<ResponseField name="cart" type="Cart | null" />

<ResponseField name="status" type="'idle' | 'loading' | 'ready' | 'error'" />

<ResponseField name="error" type="Error | null">Captured from the last failed mutation. Mutations do not throw.</ResponseField>
<ResponseField name="pending" type="number">Mutations in flight.</ResponseField>
<ResponseField name="itemCount" type="number">Sum of quantities, for a badge.</ResponseField>
<ResponseField name="revision" type="number">Bumped once per successful mutation made through this store, never by a load or refresh. Compare it across renders; the absolute value means nothing.</ResponseField>

### load

```ts theme={null}
store.load(): Promise<Cart | null>
```

Reads the remembered cart with `cart.current()`. **Never creates a cart**, so mounting a badge causes no write for a visitor who only browses.

### refresh

```ts theme={null}
store.refresh(): Promise<Cart | null>
```

Re-reads the cart from the server.

### add / updateItem / removeItem

```ts theme={null}
store.add(item: AddCartItemInput): Promise<Cart | null>
store.updateItem(itemId: string, quantity: number): Promise<Cart | null>
store.removeItem(itemId: string): Promise<Cart | null>
```

Resolve to `null` on failure, with the error in the snapshot. `add()` creates the cart on first use. `updateItem(itemId, 0)` is translated into a remove.

### clear / clearCart

```ts theme={null}
store.clear(): Promise<void>
store.clearCart(): Promise<void> // alias
```

Empties the basket through `cart.clear()`. A no-op (no request, no revision bump) when there is no cart.

### buyNow

```ts theme={null}
store.buyNow(item: AddCartItemInput, input: StartCheckoutInput): Promise<BuyNowResult>
```

Adds the item to the remembered cart and starts a checkout, keeping the store in step so a badge on a page the shopper comes back to is right. Returns the [start result](/kit/reference/checkout) plus `cart`. It does not navigate; the UI layers do.

### onMutation

```ts theme={null}
store.onMutation(listener: (event: CartMutationEvent) => void): () => void
```

Be told about every **successful** mutation made through this store, with the cart before and after. Returns an unsubscribe. Never emitted for `load`, `refresh` or `hydrate` (something was read, nothing changed), nor for a failed mutation. It is what the analytics hub diffs into `add_to_cart` and `remove_from_cart`.

<ResponseField name="type" type="'add' | 'update' | 'remove' | 'clear' | 'buy-now'" />

<ResponseField name="before" type="Cart | null">The cart before the change.</ResponseField>
<ResponseField name="after" type="Cart | null">The cart after the change; `null` after `clear`.</ResponseField>

### hydrate

```ts theme={null}
store.hydrate(cart: Cart | null): void
```

Adopts a cart fetched elsewhere, for example by TanStack Query or a server action, so every subscriber updates.

```ts theme={null}
const add = useMutation({
  mutationFn: (item: AddCartItemInput) => shopkit.cart.add(item),
  onSuccess: (cart) => cartStore.hydrate(cart),
})
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.