Skip to main content
This quickstart connects a Next.js App Router project directly to the public Storefront API. No BFF or browser API key is required.

Prerequisites

  • Node.js 20+
  • A published merchant slug and location ID
  • The exact browser origin for local, preview, and production deployments registered with Crave
  • A Stripe publishable key when you add checkout

1. Create the app and install the SDK

2. Configure the public origin

Create a .env.local file with public configuration only:
The hosted cravejs-storefront-template does not duplicate tenant selection in production environment variables. It derives the merchant from the controlled storefront host/subdomain and the location from the [locationId] route. Keep the prefixed merchant and location variables only for a standalone quickstart or local override. The hosted template also derives its canonical origin from the request host; set NEXT_PUBLIC_STOREFRONT_URL only when an explicit metadata override is required.
Do not put Crave private API keys, Stripe secret keys, provider credentials, cart capabilities, customer JWTs, or receipt capabilities in public environment variables.
The SDK requires an explicit baseUrl; it never derives an origin from a credential.

3. Create the browser client and scoped stores

The environment, merchant, and location scopes prevent a staging token or one merchant’s token from being reused by another storefront. Persist the JWT returned by customer.verifyOtp with customerTokenStore.set(token). Capture a receipt capability from the URL fragment, remove the fragment immediately, and keep it with receiptTokenStore.set(receiptId, token) only while the receipt is needed.

4. Create a separate server client

Use the server client only for anonymous published merchant, location, menu, product, distance, order-time, and gratuity calls. It deliberately has no sessionStore or getAuthToken. Never import the browser client into a Server Component, Route Handler, or Server Action.

5. Start a cart and mutate it

The SDK stores the returned cart capability, sends it only in a header, updates the saved revision from ETag, and generates idempotency keys. A CART_CONFLICT is surfaced to your UI; reload the cart before asking the shopper to retry. Clear the guest capability after claim, deletion, expiry, or terminal checkout handling; clear the customer JWT on logout and receipt capabilities after terminal display.

6. Complete checkout safely

For a signed-in shopper who still has a guest-capability cart, opt into the customer context when discount validation or provider customer association needs it:
Omit includeCustomerContext for a guest checkout. A claimed cart already uses the customer JWT because its guest capability has been cleared. Confirm the Render the payment provider named by the returned session, then poll storefront.checkout.getOrderResult(locationId, cart.id). Handle all four states: payment_pending, order_pending, completed, and failed. Clear the cart session only after terminal handling.

7. Register every browser origin

Register the exact origin for local development, each stable preview, and production before testing browser calls. An origin includes the scheme, hostname, and any non-default port. Do not request a wildcard and do not send a cart capability or customer JWT while working around a CORS failure. See Deployment.

Next steps

Manage Cart

Add fulfillment, discounts, gratuity, and conflict handling.

Checkout Flow

Confirm payment and poll the authoritative order result.

Storefront SDK

Review the complete typed client surface.

Deployment

Configure exact origins and production environment variables.