Preview & sample data
You can preview any template in your theme without a live store. The
platform renders your Liquid over the dummy fixtures you ship in
sample-data/*.json - the same render path used in production, so what you see
in preview is what a real store gets.
Sample-data fixtures
Ship dummy data under sample-data/. These fixtures are not optional decoration
- the automated review gate renders every template over them, so a bundle whose fixtures are missing or misnamed fails review.
The filenames are exact. The loader looks for these keys and nothing else:
sample-data/
├── shop.json # REQUIRED - the shop object. No shop, no preview.
├── products.json # array of products; products[0] powers the PDP preview
├── collections.json # array of collections; collections[0] powers the
│ # collection preview, and each entry is also exposed
│ # as collections['<handle>'] globally
├── menus.json # object keyed by handle: "main-menu", "footer-menu"
├── search.json # { "terms": "...", "results": [ ...products ] }
├── page.json # { "title", "handle", "content" } for the page preview
├── cart.json # a populated cart (defaults to an empty cart)
├── customer.json # a customer object, or literally `null`
├── settings.json # sample merchant setting overrides (optional)
└── globals.json # global section settings (optional)
shop.jsonis mandatory, and the plural filenames matter. A fixture namedproduct.jsonorcollection.json(singular) is simply not read - your preview renders with no products and the gate reports empty pages. And with nosample-data/shop.jsonat all, every template in the gate fails with “Missing sample-data/shop.json preview fixture” - the single most common first-submission failure.
shop.json must include at least a string name and a string currency.
Each fixture matches the shape documented in the
Liquid context reference. For example, each entry in
products.json follows the product object - title, handle, price (in
paise), options[], variants[], and so on.
Keep fixtures realistic but small. Each sample-data/*.json is capped at
512 KB, and the whole sample-data/ directory at 2 MB; exceeding either
fails the parse.
The standalone preview endpoint
The platform renders a preview via a public, unauthenticated endpoint:
GET /public/themes/:slug/preview/:template?version=<semver>:slug- your theme’s slug.:template- the template to render (index,product,collection,search,page,wishlist,404, …).version- optional; pins a specific published version. Omit for the latest.
It returns text/html rendered from your bundle over the dummy sample-data
fixtures. No tenant, no database, no customer data is involved - so previewing is
always safe and side-effect free.
The preview’s bundle assets (CSS / JS / fonts / images) are served from
/assets/:slug/:version/*, which is exactly what the asset_url filter emits -
so a preview renders fully styled.
Local preview via the CLI
During development, qm theme dev runs the same render library locally against
your sample-data fixtures, so you get an instant local preview loop without
publishing anything.
qm theme dev
# renders each template over sample-data and serves them locallyBecause the CLI local preview, the marketplace preview, and the automated review
gate all share the identical render path, a template that renders correctly in
qm theme dev renders correctly everywhere.
What to preview
At minimum, preview and read every template at all three breakpoints (1440 / 768 / 375 px):
- Home (
index) - Product (
product) - including sold-out and unavailable-combination states - Collection (
collection) - with filters applied and an empty result - Search (
search) - a hit and the empty state - A content page (
page) - Wishlist (
wishlist) - populated and empty - 404
Do not pattern-match on “looks right.” Exercise sort, filter, pagination, search, and add-to-cart - broken interactions do not show in a static screenshot.