> ## Documentation Index
> Fetch the complete documentation index at: https://docs.joinmarkt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a custom theme

> Copy a catalog theme into the editor, or build the same folder from scratch for React, Liquid, or Vite.

A theme is a folder of JSON and section code. The editor opens a copy of that folder. Visual and Code edit the same files. Publish builds that copy onto the store.

Catalog themes on disk live in `templates/themes/<id>/`. `theme.json` `id` matches the folder name. `runtime` is `react`, `liquid`, or `vite`.

| `runtime` | Folders |
| - | - |
| `react` | Atelier, Glitch, Pulse |
| `liquid` | Brutal, Meadow, Noir |
| `vite` | Orbit, Prism, Vapor |

The trees below are Glitch, Noir, and Orbit, cut down to one section (`hero`) plus the shared files those kits actually contain.

## Seller steps

**1. Hub**

Open the theme hub and pick a catalog theme. The hub lists the folders above.

**2. Catalog copy**

Copy that folder into a project the editor can open:

```http theme={null}
POST /api/stores/{storeId}/themes
Content-Type: application/json

{ "themeId": "glitch", "title": "Glitch" }
```

`themeId` is the folder name. `title` is optional (2–80 characters). When it is omitted, the project uses the theme name. The caller is signed in and has design permission on the store. A store holds at most 10 theme projects.

The response is `{ "pageId", "editorPath" }`. `editorPath` is `/theme/{storeId}/{pageId}`. The saved draft format is `join-markt-theme/v2`. Its `files` are the folder on disk (`theme.json`, `templates/`, `groups/`, `config/`, `src/sections/`, and the rest of that kit).

The hub actions that add a project, or that apply an uploaded ZIP, save a different draft. Visual and Code open this copy.

**3. Edit**

Open `editorPath` on the builder host. Visual and Code write the same files. Switch in the top bar. Setting edits show in the preview immediately. Code edits rebuild.

**4. Publish**

**Publish** in the top bar checks the files, builds them, and serves that build on the store subdomain and custom domain. The live store stays on the previous publish until this one succeeds.

> [!WARNING]
>
> Uploading a ZIP does not open Visual or Code. The editor still expects the folder in the next section, stored as the `files` of a `join-markt-theme/v2` draft. Put that folder in `templates/themes/<id>/` (`theme.json` `id` equal to `<id>`), then use the catalog copy above.

## From an empty folder

Create the folder, then copy it with the request above. Every kit's `index.html` loads `/src/platform/entry.tsx`. That file is not in the theme folder. The platform supplies it when the draft is built.

A folder is accepted as a kit when it has `theme.json` (`engine`, `id`, `name`, `version`), `templates/index.json`, `index.html` loading that script, and at least one `src/sections/<type>/schema.json`. Publish also requires the section design file (`index.tsx`, `index.liquid`, or `index.ts`). The trees include the other files those three kits ship, because the editor reads them (pages, header, footer, theme settings).

`templates/index.json` is a map of section instances plus `order`. Section `type` is the folder name under `src/sections/`. This is the `hero` instance from Glitch. A one-section home page keeps this object and sets `"order": ["hero"]`.

```json templates/index.json theme={null}
{
  "sections": {
    "hero": {
      "type": "hero",
      "settings": {},
      "blocks": {
        "shop_now": {
          "type": "button",
          "settings": { "label": "Shop now", "url": "/products", "style": "primary" }
        },
        "reviews": {
          "type": "button",
          "settings": { "label": "Read reviews", "url": "/reviews", "style": "secondary" }
        }
      },
      "block_order": ["shop_now", "reviews"]
    }
  },
  "order": ["hero"]
}
```

Glitch's real `order` is `hero`, `featured_products`, `group_list`, `features`, `testimonials`, `faq`, `cta_banner`. Noir's is `hero`, `marquee`, `featured_products`, `group_list`, `image_with_text`, `promises`, `testimonials`, `faq`, `cta_banner`. Orbit's is `hero`, `stats`, `featured_products`, `group_list`, `features`, `testimonials`, `faq`, `cta_banner`. Each id in `order` has a matching object under `sections`.

The other page files in every kit use that same shape: `templates/404.json`, `templates/cart.json`, `templates/group.json`, `templates/policy.json`, `templates/product.json`, `templates/products.json`, `templates/reviews.json`, `templates/support.json`. File roles: [Theme structure](https://docs.joinmarkt.com/themes/structure.md).

### React (Glitch)

```json theme.json theme={null}
{
  "engine": "join-markt/v2",
  "id": "glitch",
  "name": "Glitch",
  "version": "1.0.0",
  "author": "Join Markt",
  "description": "Dark cyberpunk storefront: acid-lime and hot-magenta signal colours, monospace display type, scanlines, RGB-split glitch headings, terminal product cards and a live ticker.",
  "runtime": "react"
}
```

```txt theme={null}
theme.json
index.html
tailwind.config.ts
LICENSE
config/
  settings_schema.json
  settings_data.json
groups/
  header.json
  footer.json
templates/
  index.json
  product.json
  products.json
  group.json
  cart.json
  reviews.json
  support.json
  policy.json
  404.json
public/
  pm/
    apple-pay.svg
    bancontact.svg
    crypto.svg
    google-pay-alt.svg
    ideal.svg
    mastercard.svg
    paypal.svg
    pix.svg
    visa-alt.svg
src/
  theme.tsx
  styles/
    theme.css
  components/
    links.tsx
    glitch-text.tsx
    cart-drawer.tsx
  sections/
    hero/
      schema.json
      index.tsx
```

Glitch's `src/theme.tsx` is the layout (default export, header group, page, footer group). It imports `src/styles/theme.css` and `src/components/cart-drawer.tsx`. `src/sections/hero/index.tsx` is a default export and imports `src/components/links.tsx` and `src/components/glitch-text.tsx`. The rest of Glitch's `src/components/` (`cards.tsx`, `cart.tsx`, `empty-state.tsx`, `grid.tsx`, `icons.tsx`, `rating.tsx`, `search-dialog.tsx`, `section-heading.tsx`, `sheet.tsx`, `product/gallery.tsx`, `product/purchase.tsx`, `product/tabs.tsx`) is there for other sections. A new section is another folder beside `hero` with `schema.json` and `index.tsx`.

`index.html` in Glitch:

```html index.html theme={null}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <meta name="color-scheme" content="dark" />
    <meta name="theme-color" content="#07080a" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700;800&display=swap" />
    <title>Store</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/platform/entry.tsx"></script>
  </body>
</html>
```

### Liquid (Noir)

```json theme.json theme={null}
{
  "engine": "join-markt/v2",
  "id": "noir",
  "name": "Noir",
  "version": "1.0.0",
  "author": "Join Markt",
  "description": "Dark, premium minimalist storefront: charcoal canvas, warm gold accents, serif headlines and slow cinematic motion.",
  "runtime": "liquid"
}
```

```txt theme={null}
theme.json
index.html
tailwind.config.ts
LICENSE
config/
  settings_schema.json
  settings_data.json
groups/
  header.json
  footer.json
templates/
  index.json
  product.json
  products.json
  group.json
  cart.json
  reviews.json
  support.json
  policy.json
  404.json
public/
  pm/
    apple-pay.svg
    bancontact.svg
    crypto.svg
    google-pay-alt.svg
    ideal.svg
    mastercard.svg
    paypal.svg
    pix.svg
    visa-alt.svg
snippets/
  cart-body.liquid
  group-card.liquid
  icon.liquid
  product-card.liquid
  section-heading.liquid
src/
  theme.css
  theme.ts
  lib/
    commerce.ts
    dom.ts
    motion.ts
  sections/
    hero/
      schema.json
      index.liquid
      script.ts
```

`index.html` is the same shell as Glitch, with Noir's own `theme-color`. `src/sections/hero/index.liquid` is the markup. `script.ts` exports `mount`. `tailwind.config.ts` scans `src/**/*.{ts,tsx,liquid}` and `snippets/**/*.liquid`. Another section is another folder beside `hero` with `schema.json`, `index.liquid`, and optionally `script.ts`.

### Vite (Orbit)

```json theme.json theme={null}
{
  "engine": "join-markt/v2",
  "id": "orbit",
  "name": "Orbit",
  "version": "1.0.0",
  "author": "Join Markt",
  "description": "Dark space-age storefront: deep navy, cyan and amber accents, a live starfield hero, orbiting product rings and glass panels.",
  "runtime": "vite"
}
```

```txt theme={null}
theme.json
index.html
tailwind.config.ts
LICENSE
config/
  settings_schema.json
  settings_data.json
groups/
  header.json
  footer.json
templates/
  index.json
  product.json
  products.json
  group.json
  cart.json
  reviews.json
  support.json
  policy.json
  404.json
public/
  pm/
    apple-pay.svg
    bancontact.svg
    crypto.svg
    google-pay-alt.svg
    ideal.svg
    mastercard.svg
    paypal.svg
    pix.svg
    visa-alt.svg
src/
  theme.css
  theme.ts
  lib/
    cart.ts
    dom.ts
    icons.ts
    motion.ts
    section.ts
    store.ts
  sections/
    hero/
      schema.json
      index.ts
```

`index.html` is the same shell as Glitch, with Orbit's own `theme-color`. `src/sections/hero/index.ts` exports `mount` and `update` (Orbit builds those with `defineSection` from `src/lib/section.ts`). `tailwind.config.ts` scans `index.html` and `src/**/*.{ts,tsx}`. Another section is another folder beside `hero` with `schema.json` and `index.ts`.

How a section is written, including blocks: [Runtimes](https://docs.joinmarkt.com/themes/runtimes.md). Setting types: [Section schema](https://docs.joinmarkt.com/themes/section-schema.md).

## What the platform owns

The theme draws the storefront. The platform owns the commerce data, the purchase flow, and the storefront runtime.

| Platform | Theme |
| - | - |
| Products, groups, prices, stock, on-hold state | How those records are laid out |
| Cart lines, quantities, coupons | Cart markup and when it opens |
| Checkout and payment | A control that starts checkout |
| Delivery after payment | Page sections, header, footer, theme settings, CSS, section code |
| `src/platform/` (routing, data, section renderer), overlaid from `templates/_platform` on every build | `src/sections/`, `src/theme.tsx` or `src/theme.css` and `src/theme.ts`, `snippets/` |
| `vite.config.ts`, `postcss.config.mjs`, `tsconfig.json` | `theme.json`, `index.html`, `tailwind.config.ts`, `config/`, `groups/`, `templates/`, `public/` |

`groups/header.json` and `groups/footer.json` are the theme's header and footer. Policy pages use `templates/policy.json`. The theme renders them. The policy text is store content.

## Next

> [card] [Runtimes](https://docs.joinmarkt.com/themes/runtimes.md)
>
> React, Liquid, and Vite section files.

> [card] [Section schema](https://docs.joinmarkt.com/themes/section-schema.md)
>
> Setting types, presets, and limits.

> [card] [Using the builder](https://docs.joinmarkt.com/themes/builder.md)
>
> Visual, Code, and the live preview.

> [card] [Catalog](https://docs.joinmarkt.com/themes/catalog.md)
>
> Catalog themes you can copy.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.