Skip to main content

Liquid Theme Package Format

A Liquid theme package is source text plus assets. Shoppex validates it, stores the source package privately, serves public assets from the immutable artifact prefix, and renders pages with the shared Liquid core.

Required File Tree

my-theme/
  layout/
    theme.liquid
  sections/
    hero.liquid
    products.liquid
    product-buy-box.liquid
  snippets/
    product-card.liquid
    cart-drawer.liquid
  assets/
    built.css
  schema.json
  settings.json
  theme-package.json

theme-package.json

This file is mandatory. It declares every template and asset the renderer may use.
{
  "schemaVersion": 1,
  "theme": {
    "id": "my-liquid-theme",
    "name": "My Liquid Theme",
    "version": "1.0.0"
  },
  "entryTemplate": "layout/theme.liquid",
  "paths": {
    "layout": "layout/theme.liquid",
    "sections": ["sections/hero.liquid", "sections/product-buy-box.liquid"],
    "snippets": ["snippets/product-card.liquid"],
    "assets": ["assets/built.css"],
    "schema": "schema.json",
    "settings": "settings.json"
  },
  "styles": {
    "built": "assets/built.css"
  },
  "blocks": {
    "hero": {
      "kind": "static",
      "template": "sections/hero.liquid"
    },
    "buy-box": {
      "kind": "snippet",
      "template": "sections/product-buy-box.liquid",
      "requiredContext": "product"
    }
  }
}

Block Registry

blocks maps Builder block types to Liquid templates. Common fields:
FieldMeaning
kindstatic for normal sections, snippet for reusable/rendered fragments
templateDeclared Liquid file path
requiredContextOptional live context slice such as product, homeListing, catalogListing, reviews, or storeStats
Simple example: a product buy box requires product context, so the producer must build product before the section renders.

Builder Markers

Every renderable section root must include:
data-builder-block="{{ block.id }}"
data-builder-block-type="{{ block.type }}"
The root should still render when content is empty. Builder preview reconciliation needs a stable element to target.

Safe Liquid Rules

Merchant packages are treated as untrusted. Validation rejects:
  • undeclared templates or assets
  • dynamic partials such as {% render section.partial %}
  • path traversal
  • executable files such as .js, .ts, or .tsx
  • <script>, inline event handlers, javascript: URLs, and unsafe embeds
  • oversized packages
Use static renders:
{% render "snippets/product-card", item: product %}
Do not use dynamic renders:
{% render section.partial %}

Assets

assets/built.css is the required published stylesheet. It should be built from CSS that scans the Liquid sources. Use assetUrl for theme assets:
<link rel="stylesheet" href="{{ 'assets/built.css' | assetUrl: ctx }}">
Use safe_href for user-controlled URLs:
<a href="{{ settings.ctaUrl | safe_href }}">Buy now</a>

Next Steps

Core Contract

What templates may do and what TypeScript producers must own.

Build & Publish

How Shoppex validates, stores, renders, and serves Liquid packages.