Universal checkout SDK for Commerce Engine - works with React, Vue, Svelte, Solid, and vanilla JS
npm install @commercengine/checkoutUniversal checkout SDK for Commerce Engine. Works with React, Vue, Svelte, Solid, and vanilla JavaScript.
- 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
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 │
└─────────────────────────────────────────────────────────────┘
`bash`
npm install @commercengine/checkoutor
pnpm add @commercengine/checkoutor
yarn add @commercengine/checkout
`tsx
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 (
);
}
function ProductCard({ product }) {
const { addToCart } = useCheckout();
return (
);
}
// User state is also reactive
function UserGreeting() {
const { isLoggedIn, user } = useCheckout();
if (!isLoggedIn) return Guest;
return Welcome, {user?.firstName || user?.email};
}
`
`vue
`
`svelte
`
Or use individual stores for fine-grained reactivity:
`svelte
{$cartCount}
`
`tsx
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 (
);
}
`
`ts
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();
}
);
`
Pass configuration to initCheckout():
`ts
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"
// 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) => {},
onLogin: (data) => {},
onLogout: (data) => {},
onTokenRefresh: (data) => {},
onSessionError: () => {},
onError: (error) => {},
});
`
| Function | Description |
|----------|-------------|
| initCheckout(config) | Initialize checkout with configuration |destroyCheckout()
| | Destroy checkout instance and cleanup |
| Framework | Import |
|-----------|--------|
| React | useCheckout() from @commercengine/checkout/react |useCheckout()
| Vue | from @commercengine/checkout/vue |checkout
| Svelte | store from @commercengine/checkout/svelte |useCheckout()
| Solid | from @commercengine/checkout/solid |
| 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 |
| 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 |
All types are exported for convenience:
`ts
import type {
// Configuration
CheckoutConfig,
CheckoutFeatures,
Environment,
AuthMode,
SessionMode,
QuickBuyConfig,
// State
CartData,
OrderData,
UserInfo,
ErrorData,
// Auth events
AuthLoginData,
AuthLogoutData,
AuthRefreshData,
// Store types
CheckoutState,
CheckoutActions,
CheckoutStore,
} from "@commercengine/checkout/react"; // or /vue, /svelte, /solid
`
If you're migrating from the old @commercengine/react package:
Before:
`tsx
import { useCheckout } from "@commercengine/react";
function App() {
const { openCart, cartCount } = useCheckout({
storeId: "store_xxx",
apiKey: "ak_xxx",
onComplete: (order) => { ... },
});
return ;
}
`
After:
`tsx
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 ;
}
`
Key differences:
1. Initialize once at app root with initCheckout() instead of passing config to every hookuseCheckout()
2. takes no parameters - just returns state and methodsdestroyCheckout()` in app unmount
3. Cleanup with
4. Works across the entire app without prop drilling or providers
MIT