React + Vite
React enhancements with the actual SDK hooks and server-authoritative cart state
Provider
Install @behio/storefront-sdk, React and @tanstack/react-query. Pass the public
API key to the provider; it creates its own client. There is no client prop.
import { BehioProvider } from '@behio/storefront-sdk/react';
import type { ReactNode } from 'react';
export function Providers({ apiKey, children }: { apiKey: string; children: ReactNode }) {
return <BehioProvider apiKey={apiKey} storage="localStorage">{children}</BehioProvider>;
}This browser-storage setup is for direct browser SDK calls. A premium storefront must render its initial catalog and cart on the server; Vite SPA rendering alone does not meet that requirement. Use your SSR framework's per-request client/cache and pass non-secret initial data to React. With HTTP-only sessions, use your server's session-bound actions/API instead of browser token storage.
Cart interactions
import { useState } from 'react';
import { useCart } from '@behio/storefront-sdk/react';
export function CartItems() {
const { cart, isEmpty, removeItem, isRemoving, error } = useCart();
const [message, setMessage] = useState('');
if (error) return <p role="alert">The cart is unavailable. Please retry.</p>;
if (!cart) return <p role="status">Loading cart…</p>;
if (isEmpty) return <p>Your cart is empty.</p>;
async function remove(id: string) {
setMessage('');
try { await removeItem(id); }
catch { setMessage('The item could not be removed. Please retry.'); }
}
return <section>
{message && <p role="alert">{message}</p>}
{cart.items.map(item => <article key={item.id}>
<h2>{item.product.name}</h2>
<p>{item.quantity} × {item.unitPrice === null ? 'Unavailable' : item.unitPrice}</p>
<button disabled={isRemoving} onClick={() => remove(item.id)}>Remove</button>
</article>)}
{cart.bundleLines.map(line => <article key={line.id}>
<h2>{line.bundleName}</h2><p>Bundle quantity: {line.quantity}</p>
</article>)}
{cart.totalsAvailable
? <p>Goods total: {cart.grandTotal} {cart.currency}</p>
: <p>Resolve unavailable items before checkout.</p>}
</section>;
}Seed the shared cart query before rendering this component. The loading branch
is an explicit fallback for an unseeded browser integration, not a substitute for
SSR. The sample only demonstrates ordinary-item removal; add bundle actions,
localized money formatting and the remaining cart controls for a complete shop.
Use the server's quantityControls instead of assuming a quantity step of one.
useCart also exposes addItem(productId, quantity), updateQuantity(itemId, quantity), clear, applyDiscount, removeDiscount and merge. Catch failed
mutations and leave the current server snapshot intact.
Checkout and authentication
Use useCheckoutPreview().preview(input) to obtain a price review. After the
shopper accepts it, call useCheckout().createOrder({...input, previewToken}).
The pending property is isCreating. Build CheckoutInput from the actual form
and merchant settings; see Cart and checkout hooks.
Use useAuth().login(email, password) and useAuth().logout() for browser-managed
sessions. The provider restores its configured storage and notifies dependent
hooks after page reload. Do not store the entire SdkResult as an auth token or
introduce a separate storage key that the provider does not read.