## Create or Update a Cart

The `cartUpsert` method creates a new cart or updates an existing one:

```ts
// Create a new cart with an item
const cart = await commerce.cartUpsert({
  items: [{ variantId: "var_123", quantity: 1 }],
});

// Add to an existing cart
const updated = await commerce.cartUpsert({
  cartId: cart.id,
  items: [{ variantId: "var_456", quantity: 2 }],
});
```

### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `cartId` | `string` | Existing cart ID (omit to create new) |
| `items` | `array` | Items to add or update |
| `items[].variantId` | `string` | The variant to add |
| `items[].quantity` | `number` | Quantity to set |

## Get a Cart

Fetch an existing cart by ID:

```ts
const cart = await commerce.cartGet({ id: "cart_abc123" });
```

### Response

```ts
{
  id: "cart_abc123",
  items: [
    {
      variantId: "var_123",
      quantity: 1,
      price: "2500",
      product: { name: "Classic Tee", slug: "classic-tee", ... },
    }
  ],
  totalAmount: "2500",
  subtotal: "3075",
  subtotalNet: "2500",
  subtotalGross: "3075",
  totalTax: "575",
  checkoutUrl: "https://checkout.stripe.com/...",
}
```

Carts are priced before checkout, so `subtotalNet`, `subtotalGross`, `totalTax`, and `taxBreakdown` are populated as soon as the cart has items. `subtotal` follows the store's tax behavior — gross when `inclusive`, net when `exclusive`:

```ts
const { store } = await commerce.meGet();
const shown = store.taxBehavior === "inclusive" ? variant.priceGross : variant.price;
```

When the Stripe Tax module is enabled the tax fields stay `null` — Stripe computes tax at checkout. See [Cart totals](/docs/api-reference/carts#cart-totals).

## Remove an Item

Remove a specific item from the cart:

```ts
await commerce.cartRemoveItem({
  cartId: "cart_abc123",
  variantId: "var_123",
});
```

## Checkout

The cart includes a `checkoutUrl` that redirects to Stripe Checkout. In the storefront, this is used in the cart sidebar:

```tsx
<a href={cart.checkoutUrl}>Proceed to Checkout</a>
```

Stripe handles the payment flow and redirects back to your store's success page on completion.

## Cart in the Storefront

The storefront template manages cart state with a combination of React Context and Server Actions:

```tsx
// Server Action to add an item
"use server";
export async function addToCart(variantId: string) {
  const cartId = cookies().get("cartId")?.value;
  const cart = await commerce.cartUpsert({
    cartId,
    items: [{ variantId, quantity: 1 }],
  });
  cookies().set("cartId", cart.id);
  return cart;
}
```