Docs

Next.js Basic Example

Run the market-aware Better Store Next.js example storefront.

Next.js Basic Example

The examples/nextjs-basic example is a small production-shaped storefront. It is intentionally simple, but it covers the full path most commerce apps need:

  1. Load products on the server with @betterstore/sdk.
  2. Resolve the buyer market from a manually selected country.
  3. Add products to a persistent client cart with useCart.
  4. Create a market-aware checkout session on the server.
  5. Render the hosted checkout flow with CheckoutEmbed.

Run the Example

bun install
cp examples/nextjs-basic/.env.example examples/nextjs-basic/.env.local

Fill in examples/nextjs-basic/.env.local:

BETTERSTORE_SECRET=bs_secret_...
BETTERSTORE_CURRENCY=usd

Start the app:

bun --filter nextjs-basic dev

Open http://localhost:3000.

The app can build without credentials, but it will show an empty product state until BETTERSTORE_SECRET is configured.

What The Example Demonstrates

  • Loading products from Better Store.
  • Resolving a market from a selected buyer country.
  • Showing market-priced products in the resolved currency.
  • Adding products to a client-side cart.
  • Creating checkout sessions on the server with marketId and buyerCountryCode.
  • Sending only the checkout ID and checkout secret to the browser.
  • Rendering checkout with CheckoutEmbed.
  • Clearing cart state after checkout.

Files To Read First

FilePurpose
src/lib/betterstore.tsCreates the server-side Better Store SDK client.
src/lib/products.tsDefines the market-aware product query and product type.
src/components/storefront.tsxRenders products, country switching, and cart state.
src/app/actions.tsCreates market-aware checkout sessions from cart items.
src/components/checkout-client.tsxRenders CheckoutEmbed with the checkout secret.

SDK Client

The example creates the SDK client in src/lib/betterstore.ts.

import { createBetterStore } from "@betterstore/sdk";

export const betterstore = createBetterStore(process.env.BETTERSTORE_SECRET!);

This file must stay server-only. Do not import it into client components.

Product Loading

Products are loaded through src/lib/products.ts using betterstore.products.list().

The page resolves a market first:

const market = await betterstore.markets.resolve({ countryCode: "CZE" });
const products = await getFeaturedProducts(market.marketId);

The example passes marketId into the product query so the API can hide unavailable products and return market-adjusted prices.

Checkout Creation

Checkout sessions are created in src/app/actions.ts.

The server action receives cart line items and the selected market context, calls betterstore.checkout.create(), and returns the checkout session identifiers needed by the checkout page.

await betterstore.checkout.create({
  currency: market.currency,
  marketId: market.marketId ?? undefined,
  buyerCountryCode: selectedCountryCode,
  lineItems,
});

Only these values should reach the browser:

  • checkoutId
  • checkoutSecret

Checkout Rendering

The checkout page renders CheckoutEmbed from @betterstore/react.

<CheckoutEmbed
  checkoutId={checkoutId}
  config={{
    checkoutSecret,
    successUrl: "/success",
    cancelUrl: "/",
    locale: "en",
  }}
/>

The checkout-specific checkoutSecret is browser-safe for that checkout session. The private BETTERSTORE_SECRET is not browser-safe.

The embedded checkout collects separate shipping and billing addresses. Billing is set to "same as shipping" by default, and the billing form appears only when the buyer chooses a different billing address.

Environment Variables

VariableRequiredUsed byDescription
BETTERSTORE_SECRETYesServer onlyPrivate API secret for Better Store. Never expose this in browser code.
BETTERSTORE_CURRENCYYesServer onlyCurrency used when creating checkout sessions.
BETTERSTORE_API_PROXYOptionalServer/app proxyUse when API calls should go through your own application.
NEXT_PUBLIC_BETTERSTORE_CHECKOUT_PROXYOptionalBrowserBrowser-safe checkout proxy path for the example app.

Security Model

  • BETTERSTORE_SECRET is only used in server files.
  • The browser receives only the checkout-specific checkoutSecret.
  • Product reads and checkout creation happen through server components or server actions.
  • CheckoutEmbed uses the checkout secret to finish the checkout flow safely in the browser.

On this page