Theme screenshots & assets

A theme ships two kinds of images, for two different audiences:

Kind Audience Lives in Ships as
Marketplace screenshots + thumbnail Merchants browsing the theme store, reviewers screenshots/ (repo root) screenshots/ inside theme.zip
Bundled static assets (banners, images) The storefront, at render time src/themes/custom/assets/ theme/assets/ inside theme.zip

Marketplace images are only used to present the theme. Bundled assets are drawn by the theme's own components on the live storefront. Keep them separate.


1. Marketplace screenshots & thumbnail

These are the preview images on the theme's store listing — a square thumbnail for the gallery card plus full previews for the detail page.

Declare them in theme.config.json

{
  // …name, slug, version, author, description…
  "thumbnail": "./screenshots/thumbnail.png",
  "screenshots": {
    "desktop": "./screenshots/desktop.png",
    "tablet":  "./screenshots/tablet.png",
    "mobile":  "./screenshots/mobile.png",
    "dark":    "./screenshots/dark-mode.png"   // include if the theme supports darkMode
  }
}

Drop the matching files in the root screenshots/ folder.

File Purpose Recommended size
thumbnail.png Gallery card (square) 800 × 800
desktop.png Full homepage, desktop 1600 × 1000
tablet.png Homepage, tablet 1024 × 1366
mobile.png Homepage, mobile 750 × 1334
dark-mode.png Dark scheme (if features.darkMode) 1600 × 1000

Formats: .png, .jpg, .webp. All keys are optional, but a theme with no thumbnail or screenshots lists without preview imagesnpm run validate warns for each declared image that is missing, and for a theme that declares none.

npm run build stages screenshots/ into dist/screenshots/; npm run package adds it to theme.zip under screenshots/.


2. Bundled static assets (banners & images)

Images the theme itself renders — hero/marketing banners, section artwork, illustrations, icons, default placeholders. They live inside the theme and ship in the built theme.

Where they go

src/themes/custom/assets/
├── banners/     # marketing / hero banners
└── images/      # everything else

The whole assets/ folder is copied into the built theme (dist/theme/assets/) and ships in theme.zip.

Reference them from components

Use the @assets webpack alias, or a relative import:

import homeHero from "@assets/banners/home-hero.jpg";
// or
import homeHero from "./assets/banners/home-hero.jpg";

<Image src={homeHero} alt="Season sale" />

Because assets are bundled, they resolve the same in the local preview and on the live storefront — no S3 upload, no media slug.

Optional manifest

List reusable banners/images in theme.config.json so the platform (and merchants) can discover and swap them. src is relative to the theme root and must live under assets/:

{
  "assets": {
    "banners": [
      { "key": "home-hero", "src": "assets/banners/home-hero.jpg", "label": "Homepage hero", "alt": "Season sale" }
    ],
    "images": [
      { "key": "about-team", "src": "assets/images/about-team.jpg", "label": "About page team photo" }
    ]
  }
}
Field Required Meaning
key Stable id, unique across banners + images
src Path under assets/, no .. — the file must exist
label Human-readable name shown in the UI
alt Default alt text

The manifest is optional — files imported directly by components work without being listed. Use it for banners/images you want to be swappable or discoverable.


3. Validation

npm run validate (also run server-side on upload) checks:

  • thumbnail / screenshots — declared files exist (warning if missing) and are image files
  • assets.banners[] / assets.images[] — each entry has key, src, label; key is unique; src is under assets/ with no ..; and the file exists (a broken reference is a hard error)

Fix errors before packaging — the same gates run on the server at submission time.

See also