JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 17
  • Score
    100M100P100Q83993F
  • License MIT

Universal checkout SDK for Commerce Engine - works with React, Vue, Svelte, Solid, and vanilla JS

Package Exports

  • @commercengine/checkout
  • @commercengine/checkout/package.json
  • @commercengine/checkout/react
  • @commercengine/checkout/solid
  • @commercengine/checkout/svelte
  • @commercengine/checkout/vue

Readme

@commercengine/checkout

Universal checkout SDK for Commerce Engine. Works with React, Vue, Svelte, Solid, and vanilla JavaScript.

Features

  • Universal - One package for all frameworks
  • No Provider Required - Initialize once, use anywhere
  • Reactive State - Automatic UI updates when cart changes
  • Type Safe - Full TypeScript support
  • Tree Shakeable - Only bundle what you use

Architecture

Designed for large applications with multiple routes (Next.js, Remix, etc.):

  • Singleton Store - One checkout instance shared across all components and routes
  • Non-blocking Init - initCheckout() returns immediately, iframe loads in background
  • SSR Safe - Initialization only runs on client, server renders with default state
  • Zero Core Web Vitals Impact:
    • LCP: Iframe is hidden, doesn't affect largest contentful paint
    • INP/FID: No main thread blocking during initialization
    • CLS: Fixed positioning, no layout shifts
┌─────────────────────────────────────────────────────────────┐
│  App Root (layout.tsx / _app.tsx)                           │
│  └─ initCheckout() ──► Creates hidden iframe (async)        │
├─────────────────────────────────────────────────────────────┤
│  Any Component (header, product page, cart page...)         │
│  └─ useCheckout() ──► Same singleton store                  │
├─────────────────────────────────────────────────────────────┤
│  Route Changes (SPA navigation)                             │
│  └─ Checkout instance persists, no re-initialization        │
└─────────────────────────────────────────────────────────────┘

Installation

npm install @commercengine/checkout
# or
pnpm add @commercengine/checkout
# or
yarn add @commercengine/checkout

Quick Start

React / Next.js / Remix

import { useCheckout, initCheckout, destroyCheckout } from "@commercengine/checkout/react";
import { useEffect } from "react";

// Initialize once at app root
function App({ children }) {
  useEffect(() => {
    initCheckout({
      storeId: "store_xxx",
      apiKey: "ak_xxx",
      onComplete: (order) => {
        console.log("Order placed:", order.orderNumber);
      },
    });
    return () => destroyCheckout();
  }, []);

  return <>{children}</>;
}

// Use anywhere - no provider needed
function Header() {
  const { cartCount, openCart, isReady } = useCheckout();

  return (
    <button onClick={openCart} disabled={!isReady}>
      Cart ({cartCount})
    </button>
  );
}

function ProductCard({ product }) {
  const { addToCart } = useCheckout();

  return (
    <button onClick={() => addToCart(product.id, product.variantId, 1)}>
      Add to Cart
    </button>
  );
}

// User state is also reactive
function UserGreeting() {
  const { isLoggedIn, user } = useCheckout();

  if (!isLoggedIn) return <span>Guest</span>;
  return <span>Welcome, {user?.firstName || user?.email}</span>;
}

Vue / Nuxt

<script setup>
import { useCheckout, initCheckout, destroyCheckout } from "@commercengine/checkout/vue";
import { onMounted, onUnmounted } from "vue";

// Initialize once at app root (App.vue or plugin)
onMounted(() => {
  initCheckout({
    storeId: "store_xxx",
    apiKey: "ak_xxx",
  });
});

onUnmounted(() => {
  destroyCheckout();
});

// Use anywhere
const { cartCount, openCart, isReady } = useCheckout();
</script>

<template>
  <button @click="openCart" :disabled="!isReady">
    Cart ({{ cartCount }})
  </button>
</template>

Svelte / SvelteKit

<script>
  import { checkout, initCheckout, destroyCheckout } from "@commercengine/checkout/svelte";
  import { onMount, onDestroy } from "svelte";

  // Initialize once at app root (+layout.svelte)
  onMount(() => {
    initCheckout({
      storeId: "store_xxx",
      apiKey: "ak_xxx",
    });
  });

  onDestroy(() => {
    destroyCheckout();
  });
</script>

<button on:click={() => $checkout.openCart()} disabled={!$checkout.isReady}>
  Cart ({$checkout.cartCount})
</button>

Or use individual stores for fine-grained reactivity:

<script>
  import { cartCount, isReady } from "@commercengine/checkout/svelte";
</script>

<span class="badge">{$cartCount}</span>

Solid / SolidStart

import { useCheckout, initCheckout, destroyCheckout } from "@commercengine/checkout/solid";
import { onMount, onCleanup } from "solid-js";

// Initialize once at app root
function App(props) {
  onMount(() => {
    initCheckout({
      storeId: "store_xxx",
      apiKey: "ak_xxx",
    });
  });

  onCleanup(() => {
    destroyCheckout();
  });

  return <>{props.children}</>;
}

// Use anywhere
function Header() {
  const checkout = useCheckout();

  return (
    <button onClick={() => checkout.openCart()} disabled={!checkout.isReady}>
      Cart ({checkout.cartCount})
    </button>
  );
}

Vanilla JavaScript

import { initCheckout, getCheckout, subscribeToCheckout } from "@commercengine/checkout";

// Initialize
initCheckout({
  storeId: "store_xxx",
  apiKey: "ak_xxx",
  onComplete: (order) => {
    window.location.href = `/thank-you?order=${order.orderNumber}`;
  },
});

// Use anywhere
document.getElementById("cart-btn").onclick = () => {
  getCheckout().openCart();
};

// Subscribe to cart updates
subscribeToCheckout(
  (state) => state.cartCount,
  (count) => {
    document.getElementById("cart-badge").textContent = count.toString();
  }
);

Configuration

Pass configuration to initCheckout():

initCheckout({
  // Required
  storeId: "store_xxx",
  apiKey: "ak_xxx",

  // Environment (default: "production")
  environment: "production", // or "staging"

  // Or use custom URL for local development
  url: "http://localhost:8080",

  // Theme (default: "system")
  theme: "light", // or "dark" or "system"

  // Drawer direction per viewport
  drawerDirection: {
    mobile: "bottom",   // "bottom" | "top" | "left" | "right" | "modal"
    desktop: "right",   // "bottom" | "top" | "left" | "right" | "modal"
  },

  // Appearance
  appearance: {
    zIndex: 99999,
  },

  // Authentication
  authMode: "managed", // or "provided"
  accessToken: "...",  // if user already logged in
  refreshToken: "...",

  // Quick Buy (add product on init)
  quickBuy: {
    productId: "prod_xxx",
    variantId: "var_xxx", // or null
    quantity: 1,
  },
  sessionMode: "continue-existing", // or "force-new"
  autoDetectQuickBuy: false, // parse URL params

  // Features
  features: {
    loyalty: true,               // Loyalty points redemption (default: true)
    coupons: true,               // Coupon/promo codes (default: true)
    collectInStore: false,       // Collect-in-store option (default: false)
    freeShippingProgress: true,  // Free shipping progress tracker (default: true)
    productRecommendations: true, // Product recommendations in cart (default: true)
  },

  // Callbacks
  onReady: () => {},
  onOpen: () => {},
  onClose: () => {},
  onComplete: (order) => {},
  onCartUpdate: (cart) => {},
  onTokensUpdated: (data) => {},
  onLogin: (data) => {},
  onLogout: (data) => {},
  onSessionError: () => {},
  onError: (error) => {},
});

API Reference

Initialization

Function Description
initCheckout(config) Initialize checkout with configuration
destroyCheckout() Destroy checkout instance and cleanup

Hooks / Composables

Framework Import
React useCheckout() from @commercengine/checkout/react
Vue useCheckout() from @commercengine/checkout/vue
Svelte checkout store from @commercengine/checkout/svelte
Solid useCheckout() from @commercengine/checkout/solid

Return Values

Property Type Description
isReady boolean Whether checkout is ready to use
isOpen boolean Whether checkout overlay is open
cartCount number Number of items in cart
cartTotal number Cart subtotal amount
cartCurrency string Currency code (e.g., "USD")
isLoggedIn boolean Whether user is logged in
user UserInfo | null Current user info (null if not logged in)
Method Description
openCart() Open the cart drawer
openCheckout() Open checkout directly (Buy Now flow)
close() Close the checkout overlay
addToCart(productId, variantId, quantity?) Add item to cart
updateTokens(accessToken, refreshToken?) Update auth tokens

Vanilla JS Helpers

Function Description
getCheckout() Get current state and methods (non-reactive)
subscribeToCheckout(listener) Subscribe to all state changes
subscribeToCheckout(selector, listener) Subscribe to specific state
checkoutStore Direct access to Zustand store

TypeScript

All types are exported for convenience:

import type {
  // Configuration
  CheckoutConfig,
  CheckoutFeatures,
  DrawerDirection,
  DrawerDirectionConfig,
  Environment,
  AuthMode,
  SessionMode,
  QuickBuyConfig,

  // State
  CartData,
  OrderData,
  UserInfo,
  ErrorData,

  // Auth events
  AuthTokensUpdatedData,
  AuthLoginData,
  AuthLogoutData,

  // Store types
  CheckoutState,
  CheckoutActions,
  CheckoutStore,
} from "@commercengine/checkout/react"; // or /vue, /svelte, /solid

Migration from @commercengine/react

If you're migrating from the old @commercengine/react package:

Before:

import { useCheckout } from "@commercengine/react";

function App() {
  const { openCart, cartCount } = useCheckout({
    storeId: "store_xxx",
    apiKey: "ak_xxx",
    onComplete: (order) => { ... },
  });

  return <button onClick={openCart}>Cart ({cartCount})</button>;
}

After:

import { useCheckout, initCheckout, destroyCheckout } from "@commercengine/checkout/react";
import { useEffect } from "react";

// Initialize once at app root
function App({ children }) {
  useEffect(() => {
    initCheckout({
      storeId: "store_xxx",
      apiKey: "ak_xxx",
      onComplete: (order) => { ... },
    });
    return () => destroyCheckout();
  }, []);

  return <>{children}</>;
}

// Use anywhere without config
function Header() {
  const { openCart, cartCount } = useCheckout();
  return <button onClick={openCart}>Cart ({cartCount})</button>;
}

Key differences:

  1. Initialize once at app root with initCheckout() instead of passing config to every hook
  2. useCheckout() takes no parameters - just returns state and methods
  3. Cleanup with destroyCheckout() in app unmount
  4. Works across the entire app without prop drilling or providers

License

MIT