npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@storefront-ui-plugin/cart-checkout-plugin

v3.1.0

Published

Reusable cart and checkout SDK for multi-tenant React storefront applications

Readme

@ecommerce-store/cart-checkout-plugin

A small React SDK for cart state and persistence in multi-tenant storefronts. Supports local-only mode (localStorage per tenant) or API mode (sync with your backend: guest session, add/view/update/remove/clear, checkout, merge guest cart on login).

Requirements: React 18+

For flow, requirements checklist, and where to debug, see ARCHITECTURE.md.


Install

npm install @ecommerce-store/cart-checkout-plugin

Quick start

  1. Wrap your app with CartProvider and pass a tenantId (e.g. store or site id):
import { CartProvider } from "@ecommerce-store/cart-checkout-plugin";

function App() {
  return (
    <CartProvider tenantId="my-store-id">
      <YourApp />
    </CartProvider>
  );
}
  1. Use useCart in any child to read the cart and call actions:
import { useCart } from "@ecommerce-store/cart-checkout-plugin";

function ProductCard({ product }) {
  const { addItem } = useCart();

  return (
    <button
      onClick={() =>
        addItem({
          id: product.id,
          name: product.name,
          price: product.price,
          quantity: 1,
        })
      }
    >
      Add to cart
    </button>
  );
}

function CartPage() {
  const { items, summary, removeItem, updateQuantity, clearCart } = useCart();

  return (
    <div>
      {items.map((item) => (
        <div key={item.id}>
          {item.name} × {item.quantity} — ${(item.price ?? 0) * item.quantity}
          <button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button>
          <button onClick={() => updateQuantity(item.id, item.quantity - 1)}>-</button>
          <button onClick={() => removeItem(item.id)}>Remove</button>
        </div>
      ))}
      <p>Subtotal: ${summary.subtotal}</p>
      <p>Items: {summary.itemCount}</p>
      <button onClick={clearCart}>Clear cart</button>
    </div>
  );
}

API

CartProvider

| Prop | Type | Default | Description | |---------------------|----------|-----------|--------------------------------------------------| | tenantId | string | required | Tenant/store id. Cart is stored per tenant. | | storageKeyPrefix | string | "cart" | Prefix for localStorage key (local-only mode). | | children | ReactNode | required | Your app or subtree. | | apiBaseUrl | string | — | When set, enables API mode (see below). | | storeId | string | — | Required when apiBaseUrl is set. | | getHeaders | () => Record<string, string> | — | Optional. Return headers for each request (e.g. { "X-User-Id": userId } for logged-in users). |

useCart()

Returns:

| Property | Type | Description | |-----------------|-------------------------|-------------| | items | CartItem[] | Current line items. | | summary | CartSummary | { subtotal, total, itemCount }. | | tenantId | string | Current tenant id. | | isEmpty | boolean | items.length === 0. | | addItem | (item, quantity?) => void | Add or merge by id. | | removeItem | (id) => void | Remove line by id. | | updateQuantity| (id, quantity) => void| Set quantity; remove if ≤ 0. | | clearCart | () => void | Remove all items. | | isSyncing | boolean | API mode only. True while loading or mutating. | | lastError | Error \| null | API mode only. Last API error, clear on next success. | | mergeGuestCart| () => Promise<void> | API mode only. Call after login to merge guest cart into user cart. |

useCheckout()

API mode only. Returns:

| Property | Type | Description | |-----------------|-------------------------|-------------| | startCheckout | (body?) => Promise<CheckoutResponse \| null> | POST /checkout/?tenant_id&store_id (query when set); empty body unless you pass a JSON body. | | isPending | boolean | True while checkout request is in flight. | | error | Error \| null | Checkout request error. | | result | CheckoutResponse \| null | Last successful response (checkout_url, order_id, etc.). |

Types

  • CartItemid (string or number), optional name, price, quantity (defaults to 1 when adding), and any extra fields.
  • CartSummary{ subtotal, total, itemCount }.
  • CheckoutResponse — Typed response from checkout (e.g. checkout_url, order_id). Import when using useCheckout.
  • CartContextValue — Full context type if you need it.
import type { CartItem, CartSummary, CheckoutResponse } from "@ecommerce-store/cart-checkout-plugin";

API mode (backend sync)

When you pass apiBaseUrl and storeId, the plugin switches to API mode:

  • Guest session: The plugin calls POST /cart/guest/session to get a guest cart id, stores it in localStorage (key cart_guest_id:{tenantId}:{storeId}), and sends it as the X-Guest-Cart-Id header on all cart requests.
  • Logged-in users: Provide getHeaders={() => ({ "X-User-Id": currentUserId })}. The plugin sends X-User-Id instead of (or with) the guest id. After login, call mergeGuestCart() so the backend merges the guest cart into the user cart; the plugin then stops using the stored guest id and refetches the cart.
  • Endpoints used: GET /cart/view, POST /cart/add, PUT /cart/item/{item_id}, DELETE /cart/item/{item_id}, DELETE /cart/clear?tenant_id&store_id, POST /cart/merge-guest-cart, POST /checkout/?tenant_id&store_id (checkout body empty by default). Request bodies and query params use tenant_id and store_id as required by your API.

Example:

<CartProvider
  tenantId={tenantId}
  storeId={storeId}
  apiBaseUrl="https://api.example.com"
  getHeaders={() => (user ? { "X-User-Id": user.id } : {})}
>
  <App />
</CartProvider>

// After user logs in:
const { mergeGuestCart } = useCart();
await mergeGuestCart();

// Checkout:
const { startCheckout, isPending, result } = useCheckout();
const res = await startCheckout();
if (res?.checkout_url) window.location.href = res.checkout_url;

Multi-tenant behavior

  • Local-only mode: Each tenantId gets its own cart in localStorage under the key {storageKeyPrefix}:{tenantId}. Persistence runs only after hydration so the cart is not overwritten on first paint.
  • API mode: Cart is stored on the server. Guest cart id is stored per tenant+store and reused across refreshes. Use mergeGuestCart() after login to merge guest cart into the user cart.

Building from source

npm install
npm run build

Output is in dist/: ESM and CJS bundles plus TypeScript declarations.