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:
- Load products on the server with
@betterstore/sdk. - Resolve the buyer market from a manually selected country.
- Add products to a persistent client cart with
useCart. - Create a market-aware checkout session on the server.
- Render the hosted checkout flow with
CheckoutEmbed.
Run the Example
bun install
cp examples/nextjs-basic/.env.example examples/nextjs-basic/.env.localFill in examples/nextjs-basic/.env.local:
BETTERSTORE_SECRET=bs_secret_...
BETTERSTORE_CURRENCY=usdStart the app:
bun --filter nextjs-basic devOpen 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
marketIdandbuyerCountryCode. - Sending only the checkout ID and checkout secret to the browser.
- Rendering checkout with
CheckoutEmbed. - Clearing cart state after checkout.
Files To Read First
| File | Purpose |
|---|---|
src/lib/betterstore.ts | Creates the server-side Better Store SDK client. |
src/lib/products.ts | Defines the market-aware product query and product type. |
src/components/storefront.tsx | Renders products, country switching, and cart state. |
src/app/actions.ts | Creates market-aware checkout sessions from cart items. |
src/components/checkout-client.tsx | Renders 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:
checkoutIdcheckoutSecret
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
| Variable | Required | Used by | Description |
|---|---|---|---|
BETTERSTORE_SECRET | Yes | Server only | Private API secret for Better Store. Never expose this in browser code. |
BETTERSTORE_CURRENCY | Yes | Server only | Currency used when creating checkout sessions. |
BETTERSTORE_API_PROXY | Optional | Server/app proxy | Use when API calls should go through your own application. |
NEXT_PUBLIC_BETTERSTORE_CHECKOUT_PROXY | Optional | Browser | Browser-safe checkout proxy path for the example app. |
Security Model
BETTERSTORE_SECRETis 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.
CheckoutEmbeduses the checkout secret to finish the checkout flow safely in the browser.