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-tokencredential: an opaque secret (no fixed prefix), provisioned by the platform operator; platform-wide today. Server-side only; treat it like a password. - The API origin —
https://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 installThe 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# 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"| Variable | Required | What it is |
|---|---|---|
FLIGHTDECK_API_URL | yes | The commerce API origin (https://api.ruo.pro). |
FLIGHTDECK_TENANT | yes | The tenant this deployment serves (e.g. ruo-demo). |
FLIGHTDECK_STOREFRONT_TOKEN | yes | The x-storefront-token credential — an opaque secret (no fixed prefix), operator-provisioned, platform-wide today. Server-side only. |
SITE_NAME | no | Template branding. |
COMPANY_NAME | no | Template 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 devOpen 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 mainDeploy 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_TOKENVercel 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
- In Vercel: Project > Settings > Domains > Add. Add the DNS records Vercel
gives you (a
CNAMEtocname.vercel-dns.comfor a subdomain, orA/AAAAfor an apex). Vercel issues TLS automatically. - Because this deployment is env-pinned (
FLIGHTDECK_TENANTis 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. KeepFLIGHTDECK_TENANTset 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_cartcookie 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:
getProductRecommendationsis a naive “other products”,getPage/getPagesreturn empty (no CMS surface yet, so/[page]routes 404), andrevalidateis 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
- Own the SDK surface: SDK reference
- Take a real payment: Checkout
- Prefer a Flightdeck-tailored store without a fork: Getting started