Skip to main content
A code storefront is a React and Vite project with a small commerce layer, one file for editable content, and a standard Vite build.
This page assumes a project already exists on disk. To get one, export a storefront or run shoppex storefront pull. See Code storefronts and the Theme CLI.

Import and export

Move a project in and out of Shoppex as a ZIP package.

Theme CLI

Pull, push, build, and deploy from the command line.

Customer accounts

Serve sign-in and order history from your own theme.

Project structure

Every official template shares this shape. File names inside commerce/ and config/ can differ between templates, but the folders and their purpose stay the same.

The commerce layer

ForkMe has no src/commerce/. It gets the same layer, with the same hook names, from the @shoppexio/storefront-react package. The SDK script tag below applies to it unchanged.
In Nova and Clean Minimal (Code), src/commerce/ is where the storefront talks to Shoppex. It is not an npm package: no @shoppexio/* dependency appears in their package.json. index.html instead loads the Storefront SDK as a script, pinned to a version channel:
defer lets the page parse while the SDK downloads. Deferred scripts and the module bundle run in document order, so keep the tag before the bundle’s <script type="module">. The browser exposes it as window.shoppex. src/commerce/shoppex.ts wraps that global with two functions: getShoppex() also runs the SDK’s one-time setup. It uses bootstrap globals a Shoppex worker injects next to the product catalog: shopSlug, shopId, apiBaseUrl, defaultCurrency, and an optional checkoutBaseUrl. A storefront without these globals cannot price a cart or check out, so getShoppex() fails fast instead of rendering with broken commerce. Affiliate links point buyers at your storefront with ?ref=<code>. The setup in the templates’ src/commerce/shoppex.ts captures that code on page load:
Keep this call if you rewrite the commerce layer, and add it if you build from scratch — it is what makes referral discounts price into the checkout and affiliates earn their commission. checkout() and quoteCart() read the stored code by themselves afterwards; nothing else is required. Details: captureAffiliateFromUrl.
Shoppex also injects a minimal safety net into every served code storefront that stores the ?ref= code even when the theme never calls the SDK — so the discount survives a theme that forgot the call. The affiliate’s click statistics, however, only count when the theme itself calls captureAffiliateFromUrl(). Do not rely on the safety net; make the call.
The catalog and the merchant’s content arrive the same way, read by src/config/site-config.ts: Neither function makes a network request. Both read window.__SHOPPEX_INITIAL__, injected by the same worker before the page renders. For cart and checkout, use the useCart hook from src/commerce/cart.tsx instead of calling the SDK directly. A component under CartProvider gets cart state and the actions that keep it, the SDK, and the current price quote in agreement:
useCart() returns lines, count, quote, and the actions add, update, remove, clear, applyCoupon, removeCoupon, and checkout. checkout calls the SDK’s checkout() and returns { success, message, code, redirectUrl, invoiceId }.
If a product has required checkout fields, collect them before calling checkout. resolveCheckoutFields(lines, products) and checkoutNeedsDetails(fields) in checkout-fields.ts tell you when a form must run first, and what to write onto the cart lines.
src/commerce/sdk.d.ts types the full window.shoppex surface this project uses. The complete method and type reference for the SDK lives at Storefront SDK reference. When the merchant turns on the cookie banner (Settings > Cookie Banner), Shoppex holds back the integrations from the theme’s Integrations settings until the buyer allows their category. Scripts your theme bundles itself, such as an analytics snippet in index.html or a tracking package in your code, are not held back: Shoppex cannot see inside your build. Before such a script runs or sets a cookie, it must check the buyer’s choice. While the banner is on, the page exposes window.ShoppexConsent: After every choice, window receives a shoppex:consent event whose detail is the same object as get(). A stored choice from an earlier visit is applied before your bundle runs, so read get() first and listen for the event for choices made later:
Call window.ShoppexConsent from your bundle (a Vite module script) or after DOMContentLoaded: the banner script is written at the end of <body>, so an inline script in <head> runs before it exists. When a buyer withdraws a category, the page reloads, so a script that already ran does not keep running.

Editable content: content-defaults.json

src/config/content-defaults.json is the template’s own text and settings: brand name, navigation links, hero copy, theme colors, cart labels, and the content for each page section. getSiteConfig() reads this file and merges the merchant’s saved content over it. The merge is a partial override: objects merge field by field, and arrays and single values replace completely. src/config/content-schema.json describes the field types in content-defaults.json. The Design tab then renders a color picker, a select, or a number field instead of raw text, for every value it can.
Without content-defaults.json, the Design tab has no fields to show. The storefront still works. Content edits then happen in Code or with AI instead of the Design tab.

Working locally

Every template runs on the same two commands.
bun run dev on its own renders no storefront. Every route shows a configuration error instead, because the store record and the SDK bootstrap globals have no local substitute. The values the app needs are these:

See it running with real data

Templates ship no local mock for these values on purpose, so a half-built shop never looks like a working one. Two paths reach real data:
  • Push and preview. Run shoppex storefront push to send your local project, then open the storefront under Store → Storefronts. This path needs no stub and matches production exactly.
  • Write your own stub. Serve a build with a static server, drop the cdn.shoppex.io script tag, and inject the globals above before the app runs. Keep the stub out of version control. Give every global a real value, not an empty string, and keep its synchronous methods, as typed in sdk.d.ts, returning values instead of promises.

Working with an AI agent locally

A code storefront is a normal Vite project, so any agent that edits code works on it: Claude Code, Cursor, Codex. The loop is the same as the Easy lane’s Editing with AI, with source files instead of a ThemeDocument.
  1. Pull the project into an empty directory and open it in your editor.
  1. Give the agent the map first. ForkMe ships AGENTS.md at the root: the folder map, the platform rules, and how to add a section. Nova and Clean Minimal (Code) do not ship one yet; point the agent at README.md and src/config/site-config.ts instead, and ask it to read them before changing anything.
  2. Ask for one focused change, and name the check that proves it.
  1. Review the diff. The rules the agent has to keep, whether it read them or not:
  • package.json and the lockfile stay as they are. The build reinstalls them in a sandbox with no network.
  • vite.config.ts keeps base: './'.
  • The Shoppex SDK stays a <script> in index.html, never a bundled import.
  • Elements that render editable content keep data-sx-config.
  • A new or renamed enum in a parser lands in content-schema.json in the same change.
  • Merchant copy belongs in the Design tab’s saved content, not in content-defaults.json, unless the SHAPE of the content changes.
  1. Push, then check the storefront preview under Store → Storefronts. The preview renders the real store data; bun run dev alone cannot.
The dashboard’s own code agent reads the same AGENTS.md, so a rule you add to it applies to both the dashboard and your local editor.

What the build expects

Every code storefront builds the same way:
  • A build script in package.json
  • No network access while the script runs
  • Output in dist, out, or build, with an index.html at its root
See Code storefronts for the lockfile requirement and the full size limits.