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 insidecommerce/ 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.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 referral links
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:
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.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.
Cookie consent
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 inindex.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:
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 pushto 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.ioscript 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 insdk.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.- Pull the project into an empty directory and open it in your editor.
-
Give the agent the map first. ForkMe ships
AGENTS.mdat 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 atREADME.mdandsrc/config/site-config.tsinstead, and ask it to read them before changing anything. - Ask for one focused change, and name the check that proves it.
- Review the diff. The rules the agent has to keep, whether it read them or not:
package.jsonand the lockfile stay as they are. The build reinstalls them in a sandbox with no network.vite.config.tskeepsbase: './'.- The Shoppex SDK stays a
<script>inindex.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.jsonin 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.
- Push, then check the storefront preview under Store → Storefronts. The preview renders the real store data;
bun run devalone 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
buildscript inpackage.json - No network access while the script runs
- Output in
dist,out, orbuild, with anindex.htmlat its root