GuidesTutorialsHeadless with Next.js Commerce

Headless storefront: clone to live store

This is the full path from an empty directory to a live, tenant-bound store on your own Vercel account. You will clone DSCO-Co/nextjs-commerce — a fork of Vercel’s Next.js Commerce whose only external commerce dependency is @dscodotco/sdk — point it at your tenant, run it locally, deploy it, and attach a domain.

One backend, two frontends. This repo is the headless twin of the same ruo-demo store the API serves. The platform’s own fleet renderer and this Next.js Commerce fork are two frontends over one backend — the same catalog, the same checkout, the same tenant. Swapping the frontend never forks the store.

What you need

  • A tenant ref — your store’s id (the public demo uses ruo-demo).
  • A storefront token — the x-storefront-token credential: an opaque secret (no fixed prefix), provisioned by the platform operator; platform-wide today. Server-side only; treat it like a password.
  • The API originhttps://api.ruo.pro.
  • Node 18+, pnpm, a GitHub account, and a Vercel account (free tier is fine).
⚠️

The storefront token authorizes placing orders. It is read only in server code (the provider and the /api/checkout route) and is never prefixed NEXT_PUBLIC_, so it never reaches the browser bundle. Keep it that way.

Steps

Clone and install

git clone https://github.com/DSCO-Co/nextjs-commerce.git my-store
cd my-store
pnpm install

The fork uses pnpm and Next.js (App Router, React Server Components). The only structural change from upstream Vercel Commerce is the commerce provider: lib/shopify is deleted and lib/flightdeck is dropped in, exporting the same function names and normalized return types. No component or route markup changed.

Set the environment variables

Copy the example file and fill in your tenant and token:

cp .env.example .env.local
.env.local
# The commerce API origin your store reads catalog + places orders against.
FLIGHTDECK_API_URL="https://api.ruo.pro"
 
# The tenant this deployment serves. The public demo uses ruo-demo.
FLIGHTDECK_TENANT="ruo-demo"
 
# The narrow storefront credential (x-storefront-token) — an opaque secret,
# no fixed prefix, operator-provisioned and platform-wide today.
# SERVER-SIDE ONLY. Treat it like a password.
FLIGHTDECK_STOREFRONT_TOKEN="your-storefront-token"
 
# Optional template branding (unchanged from upstream Vercel Commerce).
SITE_NAME="RUO Pro Demo"
COMPANY_NAME="dsco"
VariableRequiredWhat it is
FLIGHTDECK_API_URLyesThe commerce API origin (https://api.ruo.pro).
FLIGHTDECK_TENANTyesThe tenant this deployment serves (e.g. ruo-demo).
FLIGHTDECK_STOREFRONT_TOKENyesThe x-storefront-token credential — an opaque secret (no fixed prefix), operator-provisioned, platform-wide today. Server-side only.
SITE_NAMEnoTemplate branding.
COMPANY_NAMEnoTemplate branding.

These are the variables the fork actually reads — verified against its .env.example and the lib/flightdeck provider. This fork has no SESSION_SECRET: its cart is a cookie-backed local cart re-priced from the live catalog on every read, so there is no server session to sign. (The Flightdeck starter is a different project and does use SESSION_SECRET for shopper accounts — don’t confuse the two.)

Run it locally

pnpm dev

Open localhost:3000. Catalog reads hit the API at request time — the app is fully dynamic, so catalog changes show up immediately and no network access is needed at build time.

Until the platform’s public API surface (https://api.ruo.pro/v1/...) is live, the app builds and runs but catalog reads against the demo tenant fail at request time. That is expected during the pre-live window; the repo is published ahead of the API so the integration is reviewable. It does not fall back to a fake catalog — a failed read is never a fabricated empty result.

Verify checkout wiring

The cart’s checkoutUrl points at a first-party route, app/api/checkout/route.ts, which calls store.checkout.submit through the SDK. Flightdeck takes the card directly — there is no hosted checkout — so your store owns the payment-form UI in front of that route. See Checkout for the submit body and the SDK reference for the idempotency rules.

Push to your own Git repo

Deploying from Vercel means importing from a Git remote you control. Point the clone at your own repo and push:

git remote set-url origin https://github.com/<you>/my-store.git
git push -u origin main

Deploy to Vercel

Vercel’s Deploy button clones a repo into your GitHub + Vercel and prompts for env vars in one flow. Point it at your fork and pre-list the required variables:

https://vercel.com/new/clone?repository-url=https://github.com/<you>/my-store&env=FLIGHTDECK_API_URL,FLIGHTDECK_TENANT,FLIGHTDECK_STOREFRONT_TOKEN

Vercel prompts for the three variables during import. Add SITE_NAME and COMPANY_NAME afterward if you want the branding. This is the fastest path to a live store.

Because the store is env-pinned by tenant, it renders on the ephemeral *.vercel.app URL immediately — no custom domain required to see it live. Preview and production deploys serve the same store, since both resolve FLIGHTDECK_TENANT directly regardless of the deployment URL. See Deploying to Vercel for the full checklist.

Attach a custom domain

  1. In Vercel: Project > Settings > Domains > Add. Add the DNS records Vercel gives you (a CNAME to cname.vercel-dns.com for a subdomain, or A/AAAA for an apex). Vercel issues TLS automatically.
  2. Because this deployment is env-pinned (FLIGHTDECK_TENANT is set), the store serves correctly on your domain the moment Vercel routes it — you do not need the Flightdeck-side verification step for it to work. Keep FLIGHTDECK_TENANT set to avoid an ordering dependency between DNS/TLS propagation and verification.

Full details, apex-vs-subdomain guidance, and the host-resolved case (where verification is required) are in Custom domains.

You now have a live store

Catalog, product pages, a cookie-backed cart, and one-shot checkout — the Next.js Commerce UX, backed by your Flightdeck tenant, on your own Vercel and your own domain.

Where this fork differs from upstream

  • Cart: no server-side cart or hosted checkout. The fd_cart cookie holds only { variantId, handle, quantity } (no prices); every read re-prices from the live catalog, so it is tamper-proof and discontinued lines are pruned only on a real 404.
  • Rendering: fully dynamic (force-dynamic) — catalog reads hit the API at request time; the build needs no network.
  • Documented gaps, not faked data: getProductRecommendations is a naive “other products”, getPage / getPages return empty (no CMS surface yet, so /[page] routes 404), and revalidate is a no-op (no catalog webhook yet). The adapter returns honest absence rather than inventing data.

The function-by-function mapping is documented in the fork’s lib/flightdeck/README.md. For the design behind the adapter, see the Next.js Commerce adapter page.

Next steps