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

# Theme Structure

> Reference for the directory layout of a LaunchMyStore theme

# Theme Structure

Every LaunchMyStore theme is a folder of plain text files laid out in a fixed,
Liquid-compatible directory structure. When you upload a theme ZIP, the
installer unpacks it, builds a schema index, then serves it to merchants
of that store.

This page is the canonical reference for that directory tree.

<Note>
  LaunchMyStore is **100% Liquid-compatible**. Existing Liquid themes work as-is
  when zipped and uploaded. New themes may use the `.aqua` extension for
  template files, but the two are interchangeable — themes can mix `.liquid` and
  `.aqua` files freely.
</Note>

## The full tree

A typical theme directory looks like this:

```
<theme root>/
├── assets/
│   ├── application.css
│   ├── application.js
│   ├── theme.js
│   ├── critical.js
│   ├── logo.svg
│   └── ...
├── blocks/
│   ├── text.aqua
│   ├── button.aqua
│   ├── image.aqua
│   └── ...
├── sections/
│   ├── header.aqua
│   ├── header-group.json
│   ├── footer.aqua
│   ├── footer-group.json
│   ├── featured-product.aqua
│   ├── featured-collection.aqua
│   └── ...
├── snippets/
│   ├── meta-tags.aqua
│   ├── price.aqua
│   ├── product-card.aqua
│   ├── icon-search.aqua
│   └── ...
├── templates/
│   ├── index.json
│   ├── product.json
│   ├── product.alternate.json
│   ├── collection.json
│   ├── cart.json
│   ├── page.json
│   ├── page.contact.json
│   ├── blog.json
│   ├── article.json
│   ├── search.json
│   ├── 404.json
│   ├── password.json
│   ├── list-collections.json
│   └── gift_card.aqua
├── layout/
│   ├── theme.aqua
│   └── password.aqua
├── config/
│   ├── settings_data.json
│   ├── settings_schema.json
│   └── schema-index.json
├── locales/
│   ├── en.default.json
│   ├── en.default.schema.json
│   ├── fr.json
│   ├── fr.schema.json
│   ├── de.json
│   └── ...
└── metadata.json
```

Each directory has a specific role and a strict naming convention. The
storefront's renderer caches every file in this tree, so edits only take
effect when the theme is republished.

## Top-level directories

| Directory    | Purpose                                                     | File types                         |
| ------------ | ----------------------------------------------------------- | ---------------------------------- |
| `assets/`    | Static resources served at a public URL                     | `.css`, `.js`, images, fonts       |
| `blocks/`    | Reusable block templates (Aqua/Liquid)                      | `.aqua` / `.liquid`                |
| `sections/`  | Page sections + section group manifests                     | `.aqua`, `.liquid`, `*-group.json` |
| `snippets/`  | Reusable Aqua includes (rendered via `{% render 'name' %}`) | `.aqua` / `.liquid`                |
| `templates/` | Page templates that map URLs to compositions of sections    | `.json`, `.aqua` / `.liquid`       |
| `layout/`    | Top-level HTML wrappers (`<html>`, `<head>`, `<body>`)      | `.aqua` / `.liquid`                |
| `config/`    | Theme-level settings (schema + merchant values + index)     | `.json`                            |
| `locales/`   | Translation files for storefront copy and schema strings    | `.json`                            |

There is no `partials/`, `views/`, or `components/`. Anything reusable lives
under `snippets/` or `blocks/`. The renderer ignores any files or directories
not listed above.

***

## `assets/`

The `assets/` folder holds **all static files** that the browser fetches
directly: stylesheets, JavaScript bundles, web fonts, images, SVG icons, and
anything else that isn't a Liquid template.

### URL pattern

Each file is served at:

```
https://<store-domain>/themes/<themeId>/assets/<filename>
```

…but in templates you should never hardcode that path. Always use the
`asset_url` filter, which produces a cache-busted absolute URL:

```aqua theme={null}
<link rel="stylesheet" href="{{ 'application.css' | asset_url }}">
<script src="{{ 'theme.js' | asset_url }}" defer></script>
<img src="{{ 'logo.svg' | asset_url }}" alt="Logo">
```

The renderer appends a content-hash query parameter so updated assets bypass
the CDN cache without the merchant needing to rename the file.

### File types served

* **CSS / JS**: served as-is with the correct MIME type.
* **Images** (`.png`, `.jpg`, `.webp`, `.avif`, `.svg`): served raw. For
  responsive images, prefer `image_url` / `img_url` on **uploaded** images
  rather than asset images, because the platform's image optimization only
  runs on uploaded resources.
* **Fonts** (`.woff`, `.woff2`, `.ttf`): served with long-lived cache
  headers. Reference via `@font-face` or use `font_face` filter on
  `font_picker` settings.

### Caching and versioning

Asset files are:

1. **Read once into memory** and served from a server-side cache.
2. **Served at the edge** with `Cache-Control: public, max-age=31536000,
   immutable` once a content hash is appended by `asset_url`.
3. **Invalidated automatically** when the theme is republished — every
   cached file for the theme is cleared at once.

### What does **not** go here

* **`.liquid` / `.aqua` files** — those belong in `sections/`, `snippets/`,
  `blocks/`, `templates/`, or `layout/`. The renderer will not pick up
  templates in `assets/`.
* **JSON config** — that's `config/`.
* **Merchant-uploaded media** — product images, blog post images, etc. are
  uploaded to LaunchMyStore's media library, not the theme.

***

## `blocks/`

Theme blocks are reusable, schema-driven content units that a merchant can
drop into any section that accepts them. They are the building blocks of the
new JSON section model.

Each file is a single Aqua/Liquid template plus an optional `{% schema %}`
tag.

### Naming

| File                          | Behavior                                                                                    |
| ----------------------------- | ------------------------------------------------------------------------------------------- |
| `text.aqua`                   | Public block type `text` — appears in the editor's block picker.                            |
| `_text.aqua`                  | Private block (underscore prefix). Used internally by sections; not surfaced in the picker. |
| `image.aqua`                  | Public block type `image`.                                                                  |
| `_product-media-gallery.aqua` | Private; consumed by `product-information` section.                                         |

### Example

```aqua theme={null}
{# blocks/button.aqua #}
<a
  href="{{ block.settings.link }}"
  class="btn btn--{{ block.settings.style }}"
  {{ block.lms_attributes }}
>
  {{ block.settings.label }}
</a>

{% schema %}
{
  "name": "t:names.button",
  "settings": [
    { "type": "text",   "id": "label", "label": "Label", "default": "Shop now" },
    { "type": "url",    "id": "link",  "label": "Link" },
    {
      "type": "select", "id": "style", "label": "Style",
      "options": [
        { "value": "primary",   "label": "Primary" },
        { "value": "secondary", "label": "Secondary" }
      ],
      "default": "primary"
    }
  ]
}
{% endschema %}
```

Blocks can also be nested. Inside a section block loop, `{% content_for
'blocks' %}` renders the merchant's configured children in order. See
[Templates and sections](/aqua/templates-and-sections) for the full block
composition model.

***

## `sections/`

Sections are the largest reusable unit in a theme. Each `.aqua` / `.liquid`
file under `sections/` is one section that can be referenced from a JSON
template, a section group, or a layout's `{% sections '...' %}` tag.

### File types

| File                    | Type                       |
| ----------------------- | -------------------------- |
| `featured-product.aqua` | Section template + schema  |
| `header.aqua`           | Section template + schema  |
| `header-group.json`     | Section **group** manifest |
| `footer-group.json`     | Section group manifest     |
| `_blocks.aqua`          | Private helper section     |

A section group JSON file declares an ordered list of sections that render
together — typically the header bar, announcement bar, and meganav for the
top of every page; or the footer columns and copyright row for the bottom.

```json theme={null}
{
  "type": "footer",
  "name": "Footer",
  "sections": {
    "newsletter_RqxiU3": {
      "type": "media-with-content",
      "blocks": { ... }
    },
    "footer_main": {
      "type": "footer",
      "blocks": { ... }
    }
  },
  "order": ["newsletter_RqxiU3", "footer_main"]
}
```

The layout file references the group by name:

```aqua theme={null}
<div id="header-group">
  {% sections 'header-group' %}
</div>

{{ content_for_layout }}

<div id="footer-group">
  {% sections 'footer-group' %}
</div>
```

The renderer resolves `'header-group'` → `sections/header-group.json`,
iterates `order`, and renders each section in turn.

### Section schema

Every section file may include a `{% schema %}` block defining its settings,
block whitelist, presets, and template restrictions. See
[Section Schema](/aqua/schema) for the full reference.

The schema is parsed once at install time, cached, and reused for every
render.

***

## `snippets/`

Snippets are partials — small, reusable Liquid/Aqua fragments rendered via
`{% render 'name' %}` or the legacy `{% include 'name' %}`.

```aqua theme={null}
{# snippets/price.aqua #}
{% liquid
  assign compare = product.compare_at_price
  assign current = product.price
%}
<span class="price">
  {{ current | money }}
  {%- if compare > current -%}
    <s class="price--compare">{{ compare | money }}</s>
  {%- endif -%}
</span>
```

Used like:

```aqua theme={null}
{% render 'price', product: featured_product %}
```

Variables passed via `with` / `for` / inline arguments are scoped to the
snippet. Snippets have no schema — they're pure rendering primitives.

<Note>
  Snippets do not have `{% schema %}` blocks. If you want merchant-editable
  settings, use a block (`blocks/`) or a section (`sections/`) instead.
</Note>

### App-provided snippets

When apps are installed, their `snippets/` directory is mounted into the
storefront's snippet lookup path. A `{% render 'app-snippet-name' %}` call in
a theme will resolve to the app's snippet if the theme doesn't provide its
own with the same name — theme files always win over app files.

***

## `templates/`

Templates define what renders for a given URL. Each template file maps to
exactly one page type.

### Required templates

| Template                | URL pattern                        | Global object |
| ----------------------- | ---------------------------------- | ------------- |
| `index.json`            | `/`                                | `shop`        |
| `product.json`          | `/products/<handle>`               | `product`     |
| `collection.json`       | `/collections/<handle>`            | `collection`  |
| `cart.json`             | `/cart`                            | `cart`        |
| `page.json`             | `/pages/<handle>`                  | `page`        |
| `blog.json`             | `/blogs/<handle>`                  | `blog`        |
| `article.json`          | `/blogs/<blog>/<article>`          | `article`     |
| `search.json`           | `/search`                          | `search`      |
| `404.json`              | (any unrecognized path)            | —             |
| `password.json`         | `/password` (when store is locked) | `shop`        |
| `list-collections.json` | `/collections`                     | `collections` |

### Customer account templates

When a store enables theme account pages, templates under
`templates/customers/` render the login, registration, account dashboard,
order detail, address book, and password reset/activation pages
(`customers/login`, `customers/register`, `customers/account`,
`customers/order`, `customers/addresses`, `customers/activate_account`,
`customers/reset_password`). Each can be a `.json` section template or a
plain `.aqua` / `.liquid` template. See
[Theme Customer Accounts](/aqua/customer-accounts) for the routes, forms,
and objects.

### Per-handle variants

Any template can have alternate versions selected per resource. The selector
is the **handle**, used in the URL or specified in the resource's metadata:

```
templates/product.alternate.json     → ?view=alternate or product.template=alternate
templates/page.contact.json          → /pages/contact (handle "contact")
templates/collection.featured.json   → /collections/<handle> with template "featured"
```

### Resolution order

For a URL like `/products/blue-shirt` where the product has `template:
"alternate"`:

1. `templates/product.alternate.json` — most specific (handle + per-handle)
2. `templates/product.alternate.aqua` / `.liquid` — alternate, Liquid form
3. `templates/product.json` — default JSON template
4. `templates/product.aqua` / `.liquid` — default, Liquid form
5. **Render fails** if none exist.

The renderer tries each in order and uses the first one that exists.

### JSON template shape

JSON templates declare a composition of sections:

```json theme={null}
{
  "sections": {
    "main": {
      "type": "product-information",
      "settings": { "show_vendor": true }
    },
    "recommendations": {
      "type": "product-recommendations",
      "settings": { "products_to_show": 4 }
    }
  },
  "order": ["main", "recommendations"]
}
```

See [Templates and sections](/aqua/templates-and-sections) for the full
schema.

### Liquid template fallback

A theme can also provide a plain Liquid/Aqua template (e.g.
`templates/gift_card.aqua`). The renderer treats the whole file as one
template, evaluates it with the page's global object in scope, and wraps
the result in the layout (unless `{% layout none %}` is present).

***

## `layout/`

Layouts wrap the rendered page body in an HTML shell (`<html>`, `<head>`,
`<body>`). Every page is rendered into a layout unless explicitly bypassed.

| File            | When used                                                                                                               |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `theme.aqua`    | Default — used for every page that doesn't override.                                                                    |
| `password.aqua` | Selected when the template config has `"layout":"password"`.                                                            |
| `<name>.aqua`   | Any custom layout selected via `"layout": "<name>"` in a JSON template or `{% layout '<name>' %}` in a Liquid template. |

A layout **must** include two magic variables:

* `{{ content_for_header }}` — analytics, meta tags, scripts injected by
  the platform. Place inside `<head>`.
* `{{ content_for_layout }}` — the rendered template body. Place inside
  `<body>`.

See [Layouts](/aqua/layouts) for the full contract.

***

## `config/`

Holds theme-level (non-section) configuration.

| File                         | Purpose                                                                          |
| ---------------------------- | -------------------------------------------------------------------------------- |
| `settings_schema.json`       | Schema for the **Theme settings** screen (colors, typography, layout).           |
| `settings_data.json`         | Merchant-chosen values for the above schema. Updated by the theme editor.        |
| `schema-index.json`          | Computed index of every section/block schema in the theme. Generated at install. |
| `installation-manifest.json` | (Optional) Diagnostic record of the install process. Safe to delete.             |

`settings_schema.json` follows the same input-setting format as section
schemas — see [Input Settings](/aqua/input-settings). Merchant values are
exposed in Aqua as the global `{{ settings.* }}`:

```aqua theme={null}
<body class="page-width-{{ settings.page_width }}">
  <style>
    :root {
      --color-primary: {{ settings.color_primary }};
      --font-body--family: {{ settings.body_font.family }};
    }
  </style>
```

`schema-index.json` is a build artifact — do not edit by hand. The
storefront uses it to find sections and blocks by `type` without scanning
the filesystem on every render.

***

## `locales/`

Translation files for every string the theme renders, plus every schema
label shown in the editor.

| File                         | Purpose                                                                                                         |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `en.default.json`            | Storefront copy (button labels, error messages, ARIA, etc.). The `.default` suffix marks the fallback language. |
| `en.default.schema.json`     | Schema labels for the editor (section names, setting labels).                                                   |
| `fr.json`                    | Storefront copy translations for French.                                                                        |
| `fr.schema.json`             | Schema label translations for French.                                                                           |
| `de.json` / `de.schema.json` | Same for German.                                                                                                |

Two access patterns:

* **In templates**: `{{ 'foo.bar' | t }}` looks up the active locale.
* **In schemas**: `"label": "t:names.button"` is resolved to the merchant's
  active editor locale at install time and at render time.

See [Locales](/aqua/locales) for the full I18n model.

***

## `metadata.json` (optional)

Top-level theme metadata — name, version, author, support URL. Used by the
admin theme library and marketplace listing. Optional but recommended:

```json theme={null}
{
  "name": "Symmetry",
  "version": "2.0.4",
  "author": "Acme Themes Co.",
  "documentation_url": "https://example.com/docs",
  "support_url": "https://example.com/support"
}
```

***

## File-naming summary

| Pattern                | Means                                                      |
| ---------------------- | ---------------------------------------------------------- |
| `<name>.aqua`          | Public Aqua/Liquid template                                |
| `<name>.liquid`        | Same as `.aqua` (extensions are interchangeable)           |
| `_<name>.aqua`         | Private — not exposed in the editor's section/block picker |
| `<name>-group.json`    | Section group manifest (in `sections/`)                    |
| `<type>.<handle>.json` | Per-handle template variant (in `templates/`)              |
| `<lang>.json`          | Storefront locale file                                     |
| `<lang>.schema.json`   | Schema label locale file                                   |
| `<lang>.default.json`  | Fallback locale (only one per theme)                       |

***

## What the installer does

When a merchant uploads `theme.zip`:

1. The ZIP is unpacked into a dedicated directory for the new theme.
2. The installer scans `sections/`, `blocks/`, and `templates/` for
   `{% schema %}` blocks and writes a flat lookup table to
   `config/schema-index.json`.
3. `locales/<lang>.default.json` is flattened and merged into the index so
   `t:` keys resolve at render time.
4. `settings_data.json` is initialized from `settings_schema.json`
   defaults if no merchant values exist.
5. The asset cache is warmed for the most-requested file paths.

After install, every file under the theme directory is treated as
**read-only at runtime**. Changes only happen via:

* The theme editor (writes back to `settings_data.json` and template JSON).
* A new theme upload (creates a new `themeId`, leaving the old one intact).
* The theme file APIs / CLI (which also refresh the render cache).

***

## Next steps

<CardGroup cols={2}>
  <Card title="Templates and sections" icon="brackets-curly" href="/aqua/templates-and-sections">
    How JSON templates compose sections and blocks.
  </Card>

  <Card title="Layouts" icon="frame" href="/aqua/layouts">
    The `theme.aqua` shell, `content_for_*`, and custom layouts.
  </Card>

  <Card title="Locales" icon="language" href="/aqua/locales">
    Translating storefront copy and schema strings.
  </Card>

  <Card title="Schema" icon="code" href="/aqua/schema">
    Defining section and block settings.
  </Card>
</CardGroup>
