Skip to main content

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.

Templates and Sections

A LaunchMyStore page is a template that composes sections, each of which composes blocks. This is the same model Shopify themes have used since the 2.0 release — every existing 2.0 theme works on LaunchMyStore unchanged. This page explains:
  1. How JSON templates declare page composition.
  2. How template resolution picks the right file for a URL.
  3. How sections are reused across templates.
  4. How block-level configuration is layered on top.
  5. How section groups turn the header / footer into editable units.
  6. How the legacy .liquid / .aqua template fallback works.

Anatomy of a JSON template

A JSON template is a manifest. It lists the sections to render and their display order. The renderer reads it once, looks up each section’s Aqua source from sections/<type>.aqua, merges the merchant’s settings, and emits HTML.
{
  "sections": {
    "main": {
      "type": "product-information",
      "settings": {
        "show_vendor": true,
        "show_quantity_selector": true
      },
      "blocks": {
        "title": {
          "type": "title",
          "settings": { "size": "large" }
        },
        "price": { "type": "price" },
        "buy_buttons": {
          "type": "buy-buttons",
          "settings": { "show_dynamic_checkout": true }
        }
      },
      "block_order": ["title", "price", "buy_buttons"]
    },
    "recommendations": {
      "type": "product-recommendations",
      "settings": { "products_to_show": 4 }
    }
  },
  "order": ["main", "recommendations"]
}

Required keys

KeyTypeRequiredDescription
sectionsobjectYesMap of section_id → section instance.
orderarrayYesRender order. Items must be keys in sections.
layoutstring | falseNoOverride the layout ("layout":"password" or false).
wrapperstringNoOptional wrapper element override.

Section instance

KeyTypeDescription
typestringFilename of the section template under sections/ (no extension).
settingsobjectMerchant values for the section’s schema settings.
blocksobjectMap of block_id → block instance.
block_orderarrayBlock render order.
disabledboolIf true, the section is skipped at render.
custom_cssstringOptional per-instance CSS injected with a scoped data attribute.

Block instance

KeyTypeDescription
typestringBlock type as declared in the section’s schema blocks array.
settingsobjectMerchant values for the block’s settings.
staticboolMarks the block as fixed — merchants can’t remove or reorder it.
namestringDisplay name in the editor (supports t: translations).
blocksobjectNested blocks (for blocks that themselves render blocks).

Template resolution

When a request hits a URL, the renderer picks a template file by the following rules, in order. The first one that exists wins. For /products/<handle> where the product has template_suffix: "alternate":
  1. templates/product.alternate.json
  2. templates/product.alternate.aqua
  3. templates/product.alternate.liquid
  4. templates/product.json
  5. templates/product.aqua
  6. templates/product.liquid
  7. — fail with 500 —
For URL patterns where the handle itself is a template suffix (page and customers/*), the lookup is even simpler:
/pages/contact
  → templates/page.contact.json
  → templates/page.contact.aqua
  → templates/page.json   (fallback)
The resolver runs entirely off AssetLoader.exists() (cached) and AssetLoader.read() (cached) — no filesystem scan per request after the first hit.

Selecting a template suffix

Merchants can attach a per-resource template suffix through:
  • The product / page / collection / blog / article admin form (template_suffix column on the resource).
  • The URL query parameter ?view=<suffix> (Shopify-compatible, primarily used for AJAX section fetches).

Layout override per template

A JSON template can override the layout that wraps it:
{
  "layout": "password",
  "sections": { ... },
  "order": [ ... ]
}
"layout": false disables the layout entirely — the rendered body becomes the response. See Layouts for details.

How a section renders

Take this snippet from a JSON template:
"main": {
  "type": "featured-collection",
  "settings": { "title": "New arrivals", "products_count": 6 }
}
The renderer:
  1. Opens sections/featured-collection.aqua.
  2. Reads its {% schema %} block (cached via AssetLoader.schemaCache).
  3. Builds a section object exposing section.id = "main", section.settings = { ... }, section.blocks = [...].
  4. Strips the schema block from the source.
  5. Compiles and renders the remaining template with section in scope.
Inside the section template:
{# sections/featured-collection.aqua #}
<section
  class="featured-collection color-{{ section.settings.color_scheme }}"
  {{ section.lms_attributes }}
>
  <h2>{{ section.settings.title }}</h2>

  {% if section.settings.collection != blank %}
    <div class="grid grid-cols-{{ section.settings.columns }}">
      {% for product in section.settings.collection.products
                       limit: section.settings.products_count %}
        {% render 'product-card', product: product %}
      {% endfor %}
    </div>
  {% endif %}
</section>

{% schema %}
{
  "name": "Featured collection",
  "tag": "section",
  "class": "featured-collection",
  "settings": [
    { "type": "text",       "id": "title",          "label": "Title",       "default": "Featured collection" },
    { "type": "collection", "id": "collection",     "label": "Collection" },
    { "type": "range",      "id": "products_count", "label": "Count", "min": 2, "max": 12, "step": 1, "default": 4 },
    {
      "type": "select", "id": "columns", "label": "Columns",
      "options": [
        { "value": "2", "label": "2" },
        { "value": "3", "label": "3" },
        { "value": "4", "label": "4" }
      ],
      "default": "4"
    }
  ],
  "presets": [
    { "name": "Featured collection", "settings": { "products_count": 4 } }
  ]
}
{% endschema %}
section.lms_attributes (and its alias section.shopify_attributes) emits data-section-id="main" data-section-type="featured-collection" for the editor.

Blocks inside sections

Blocks layer over sections. A section’s schema declares which block types it accepts; the JSON template instance picks values per block and lists them in block_order.

Section schema declaration

{
  "name": "Slideshow",
  "blocks": [
    {
      "type": "slide",
      "name": "Slide",
      "limit": 5,
      "settings": [
        { "type": "image_picker", "id": "image",   "label": "Image" },
        { "type": "text",         "id": "heading", "label": "Heading" }
      ]
    },
    {
      "type": "video_slide",
      "name": "Video Slide",
      "settings": [
        { "type": "video_url", "id": "video", "label": "Video URL" }
      ]
    },
    { "type": "@app" }
  ],
  "max_blocks": 8
}
{ "type": "@app" } reserves a slot for any storefront block installed by an app extension.

Rendering blocks

{% for block in section.blocks %}
  <div {{ block.lms_attributes }} class="slide slide--{{ block.type }}">
    {% case block.type %}
      {% when 'slide' %}
        <img src="{{ block.settings.image | image_url: width: 1200 }}" alt="">
        <h2>{{ block.settings.heading }}</h2>
      {% when 'video_slide' %}
        {{ block.settings.video | external_video_tag }}
      {% when '@app' %}
        {% content_for 'block', id: block.id, type: block.type %}
    {% endcase %}
  </div>
{% endfor %}
section.blocks is the ordered iterator (matches block_order). Looking up by id (section.blocks['title']) is also supported.

Static blocks

A block instance with "static": true is locked — merchants can’t move, duplicate, or delete it in the editor, and it always renders at its declared position. Use this for blocks that are structurally required (the product title on a product page, for example):
"blocks": {
  "media-gallery": {
    "type": "_product-media-gallery",
    "static": true,
    "settings": { "media_columns": "two" }
  }
}
Private block types (prefixed with _) combined with "static": true is the canonical way to ship “fixed” elements that the merchant can still configure but can’t remove.

Section reuse across templates

Sections are theme-wide. The same featured-collection.aqua file can appear in index.json, collection.json, and page.welcome.json, each with different settings.
// templates/index.json
{
  "sections": {
    "hero": { "type": "image-banner", "settings": { "title": "Summer" } },
    "featured": {
      "type": "featured-collection",
      "settings": { "title": "New arrivals", "products_count": 8 }
    }
  },
  "order": ["hero", "featured"]
}
// templates/page.welcome.json
{
  "sections": {
    "welcome_text": { "type": "rich-text", "settings": { ... } },
    "featured": {
      "type": "featured-collection",
      "settings": { "title": "Bestsellers", "products_count": 4 }
    }
  },
  "order": ["welcome_text", "featured"]
}
The renderer doesn’t deduplicate — each instance gets its own compiled context and its own DOM subtree.

Restricting section availability

A section’s schema can opt out of certain templates via enabled_on / disabled_on:
{
  "enabled_on":  { "templates": ["index", "collection"], "groups": ["header", "footer"] },
  "disabled_on": { "templates": ["password", "cart"] }
}
The editor uses these to filter the “Add section” picker. The renderer will still execute a section that ended up on a disallowed template — these restrictions are advisory, not enforced.

Section groups

A section group is an ordered list of sections that travel together, declared as a JSON manifest under sections/. Most themes have at least header-group.json and footer-group.json.
// sections/header-group.json
{
  "type": "header",
  "name": "Header",
  "sections": {
    "announcement-bar": {
      "type": "announcement-bar",
      "settings": { "message": "Free shipping over $50" }
    },
    "header_main": {
      "type": "header",
      "settings": { "show_search": true, "logo_width": 120 }
    }
  },
  "order": ["announcement-bar", "header_main"]
}
The shape is identical to a JSON template, with an added type (the group name) and name (label shown in the editor).

The {% sections '<group>' %} tag

To render a group, use the sections tag from any Liquid/Aqua context — usually the layout:
{# layout/theme.aqua #}
<body>
  <div id="header-group">
    {% sections 'header-group' %}
  </div>

  <main id="MainContent" role="main">
    {{ content_for_layout }}
  </main>

  <div id="footer-group">
    {% sections 'footer-group' %}
  </div>
</body>
The tag loads sections/<name>-group.json, then iterates the order array and renders each section in turn, just like a JSON template.
{% sections '...' %} (plural) renders a group. {% section '...' %} (singular, legacy) renders a single static section file. Most themes prefer the group form because it gives merchants a single-button entry to reorder header/footer elements in the editor.
You can ship any group your theme needs:
  • sections/overlay-group.json — global modals, cart drawers, search popups.
  • sections/aside-group.json — sidebar widgets for collection pages.
  • sections/popups-group.json — newsletter / age gate / cookie banner.
Reference them with {% sections 'overlay-group' %} in the layout. The editor surfaces each group as a separately-editable region.

Liquid template fallback

For pages that don’t fit the JSON model, a theme can ship a plain .aqua / .liquid template that is the whole page body:
{# templates/gift_card.aqua #}
{% layout 'gift_card' %}

<div class="gift-card">
  <h1>{{ gift_card.id }}</h1>
  <p>Balance: {{ gift_card.balance | money }}</p>
  <img
    src="{{ gift_card.qr_identifier | qr_code }}"
    alt="QR code for redeeming this gift card"
  >
</div>
The renderer:
  1. Detects {% layout '<name>' %} if present and switches the wrapper layout (layout/<name>.aqua).
  2. Detects {% layout none %} and renders the file body without any layout — useful for AJAX endpoints.
  3. Compiles the file once and renders with the page’s global object in scope (e.g. gift_card for templates/gift_card.aqua).
You can mix forms within one theme: keep index.json as a section composition but render gift_card.aqua as a single-file template.

Worked example: product.json

A minimal product page:
{
  "sections": {
    "main": {
      "type": "product-information",
      "blocks": {
        "media":   { "type": "_product-media-gallery", "static": true },
        "title":   { "type": "title" },
        "price":   { "type": "price" },
        "variant": { "type": "variant-picker" },
        "buy":     {
          "type": "buy-buttons",
          "settings": { "show_dynamic_checkout": true }
        },
        "description": {
          "type": "description",
          "settings": { "show_full": true }
        }
      },
      "block_order": ["media", "title", "price", "variant", "buy", "description"]
    },
    "related": {
      "type": "product-recommendations",
      "settings": { "products_to_show": 4, "intent": "related" }
    },
    "recently_viewed": {
      "type": "product-recommendations",
      "settings": { "products_to_show": 4, "intent": "recently_viewed" }
    }
  },
  "order": ["main", "related", "recently_viewed"]
}
Rendered against the URL /products/blue-shirt:
  1. The renderer resolves templates/product.json.
  2. Fetches the product (handle blue-shirt) and exposes it as product.
  3. Renders sections in order: mainrelatedrecently_viewed.
  4. main is product-information.aqua with the schema-driven blocks above; the section’s loop renders title → price → variant → buy → description.
  5. The result is concatenated as content_for_layout and wrapped in layout/theme.aqua.
The same product-information section file is reused on templates/product.alternate.json with different settings and a different block order — no duplication of the section’s HTML or schema.

Patterns

Per-handle landing pages

For /pages/about, ship templates/page.about.json with custom sections. The default page.json still handles every other page.

Themed collection pages

Add templates/collection.featured.json with a different hero and filtering UI, then let merchants set template_suffix = "featured" on selected collections.

Section sharing between template types

Define sections/newsletter.aqua once and reference it from index.json, page.json, cart.json, and footer-group.json. Merchants edit each instance independently.

AJAX section fetches

/products/<handle>?section_id=related returns just that section’s HTML. Middleware intercepts and rewrites to /api/themes/render-section. Use this for “Load more” / “Quick view” / cart drawer updates without a full page navigation.

Next steps

Schema

Define a section’s settings, blocks, and presets.

Input settings

Every available input type for schemas.

Layouts

Wrap the rendered body in HTML.

Tags

{% section %}, {% sections %}, {% render %}, and friends.