Skip to content
Twilight React Playground
ثيم رائدaren

cart

objectBeginnerserverbrowserlive demo

Reads a cart by its id and returns it with the loyalty prize and gift details merged into the cart object.

import { cart, CartApiResponse } from '@salla.sa/twilight-theme-engine/api/cart';

In plain words

The cart is kept by Salla, not by your theme. Adding and removing products goes through the Salla SDK (window.Salla.cart); this module only reads a cart: its items, totals and coupon.

It needs the cart's id, and only the browser knows it: the Salla SDK keeps it for the visitor. So a cart is read after the page has loaded, never while the server renders.

Signature

cart.get(cartId: number): Promise<Cart>
cart.getOrThrow(cartId: number): Promise<Cart>   // any failure → NotFoundError
cart.queries.detail(cartId: number)              // key ['cart', cartId]

interface CartApiResponse {        // the raw `data` of GET cart/{id}
  cart: Omit<Cart, 'loyalty' | 'gift'>;
  loyalty?: Cart['loyalty'];
  gift?: Cart['gift'];
}
// cart.get returns { ...data.cart, loyalty: data.loyalty ?? null, gift: data.gift ?? null }

Try it live

Your own guest cart on the demo store, read with cart.queries.detail(). The id comes from the Salla SDK.Try this: add a product in the ProductCard demo, come back and reload: loyalty and gift are always present, null when unused.
Real requests to the demo store
Storefront canvas · ar · RTL
Runs in the browser…
Controls
Show the whole Cart
What a theme writes
import { useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { cart } from '@salla.sa/twilight-theme-engine/api/cart';
import { getSallaSDK } from '@salla.sa/twilight-theme-engine/utils';

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

  useEffect(() => {
    // Browser only: the server has no cart id to ask about.
    getSallaSDK()?.cart.api.getCurrentCartId().then(setCartId);
  }, []);

  const { data } = useQuery({ ...cart.queries.detail(cartId ?? 0), enabled: cartId !== null });
  if (!data) return null;

  return <span className="mini-cart">{data.count}</span>;
}

Example

app/components/cart/CartTotal.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';
import { getSallaSDK } from '@salla.sa/twilight-theme-engine/utils';

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

  useEffect(() => {
    // Browser only: the Salla SDK knows the visitor's cart id.
    getSallaSDK()?.cart.api.getCurrentCartId().then(setCartId);
  }, []);

  const { data } = useQuery({ ...cart.queries.detail(cartId ?? 0), enabled: cartId !== null });
  if (!data) return null;

  return <span className="cart-total">{format(data.total)}</span>;
}

How it behaves

  • Endpoint: GET cart/{id}. It works for guest carts; the customer token is sent when there is one.

  • The engine cart page gets the id with window.Salla.cart.api.getCurrentCartId() in an effect and runs useQuery({ ...cart.queries.detail(cartId!), enabled: !!cartId }).

  • In the Salla SDK source, getCurrentCartId() asks the API for the latest cart when it has no id stored, which creates an empty cart for a new visitor.

  • Adding or removing items through the SDK does not refresh this query. Refetch it after the SDK reports a change.

Gotchas

  • There is no cart id on the server, so a cart can never be part of the server-rendered HTML. Keep the query disabled until the id arrives; cart.queries.detail(0) would otherwise request cart/0.

  • getOrThrow turns every failure, a timeout included, into NotFoundError and the 404 page. For a cart widget use get or the query and handle the error.

Related

Source and docs