Built for Jualify theme authors

Ship storefront themes
without the guesswork.

A focused reference for Jualify Liquid API v1—package structure, templates, objects, filters, forms, schemas, and secure commerce boundaries that keep themes safe.

Browse 45 reference cards across 9 categories.

9 categories 45 copy-ready references API v1 documented contract
01

Start here

Quick start

The smallest useful map from a blank folder to a valid Jualify theme package.

Syntax

Output, logic, filters

Output uses double braces. Logic uses tag delimiters. Filters transform values.

{{ product.title }}
{% if product.available %}In stock{% endif %}
{{ product.price | money }}
02

Files that fit together

Theme structure

Jualify uses a manifest, JSON templates, Liquid layouts, ordered sections, snippets, settings, locales, and assets.

theme.json

Manifest

Declare the Liquid engine, API version, default layout, capabilities, and every supported template.

{
  "name": "Aurora",
  "handle": "aurora",
  "version": "1.0.0",
  "author": "Your studio",
  "engine": "liquid",
  "theme_api_version": "1",
  "layout": "theme",
  "capabilities": ["ecommerce", "json_templates", "sections"],
  "templates": ["index", "collection", "product", "cart"]
}
templates/*.json

Ordered JSON template

Every ordered section ID must exist and its type must map to a Liquid section file.

{
  "layout": "theme",
  "sections": {
    "header": {"type": "header", "settings": {}},
    "hero": {"type": "hero", "settings": {"heading": "Hello"}},
    "footer": {"type": "footer", "settings": {}}
  },
  "order": ["header", "hero", "footer"]
}
layout/theme.liquid

Default layout shell

Render application header content and the ordered template sections in the layout.

<!doctype html>
<html lang="{{ localization.language.code }}">
<head>
  <meta charset="utf-8">
  <title>{{ page_title }}</title>
  {{ content_for_header | raw }}
  <link rel="stylesheet" href="{{ 'theme.css' | asset_url }}">
</head>
<body>{{ content_for_layout | raw }}</body>
</html>
Templates

Supported handles

  • index
  • collection
  • product
  • cart
  • checkout
  • order-success
  • search
  • page
  • password
  • 404
  • customers/*
03

Language essentials

Liquid basics

Core Liquid provides variables, conditions, loops, capture, comments, and safe output escaping.

Variables

Assign and capture

{% assign card_title = product.title %}
{% capture price_label %}
  {{ product.price | money }}
{% endcapture %}

<h3>{{ card_title }}</h3>
<span>{{ price_label }}</span>
Logic

Conditionals

{% if product.available %}
  <span>Ready to ship</span>
{% elsif product.inventory_policy == 'continue' %}
  <span>Pre-order</span>
{% else %}
  <span>Sold out</span>
{% endif %}
Iteration

Loops with empty state

{% for item in products limit: 8 %}
  <a href="{{ item.url }}">{{ item.title }}</a>
{% else %}
  <p>No products found.</p>
{% endfor %}
OutputSafe by default

Escaping and raw

Output is escaped by default. Reserve raw for renderer-owned or Jualify-sanitized HTML.

{{ settings.heading }}
{{ product.description | raw }}
{{ page.body | raw }}
{{ content_for_layout | raw }}
04

Transform and compose

Filters & tags

These are the Jualify-specific additions to Liquid core. Use only the helpers documented in this contract.

Assets

asset_url and image_url

<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}">
<script src="{{ 'theme.js' | asset_url }}" defer></script>

<img src="{{ product.image | image_url }}"
     alt="{{ product.image.alt | default: product.title }}">
Responsive media

Choose an image size

Pass a product, collection, or blog feature-image object to image_url and choose the most appropriate stored size. An omitted, unsupported, or unavailable size safely uses the original image.

  • originalUploaded source image
  • large1024px wide
  • medium600px wide
  • thumbnail300px wide
  • small100px wide
{% assign product_image = product.image %}
<img src="{{ product_image | image_url: 'medium' }}"
     alt="{{ product.image.alt | default: product.title }}">

{{ product_image | image_url: 'thumbnail' }}
{% assign product_icon = product.image | image_url: 'small' %}
{% assign collection_card = collection.image | image_url: 'thumbnail' %}
{% assign article_icon = blog.featured_image | image_url: 'small' %}

small is a 100px-wide variant for compact UI. collection.image and blog.featured_image support the same five size options; blog.image remains a compatibility URL alias.

Prices

money

Formats a numeric value with the tenant base currency symbol and configured symbol position.

{{ product.price | money }}
{% if product.compare_at_price > product.price %}
  <s>{{ product.compare_at_price | money }}</s>
{% endif %}
Content

t, strip_html, truncate

{{ 'products.add_to_cart' | t }}
{{ product.description | strip_html | truncate: 120 }}

Locale lookup falls back to English when available.

URLs

route_url and tenant_url

{{ 'front.user.detail.view' | route_url }}
{{ '/collections/summer' | tenant_url }}

Prefer a supplied routes.* or object url value whenever one exists.

Composition

render, include, section

{% render 'product-card', product: product %}
{% include 'snippets/breadcrumbs.liquid' %}
{% section 'announcement' %}

References must be static and quoted. Dynamic paths and traversal are rejected.

FormsRequired for POST

csrf_field

<form method="post" action="{{ cart.update_url }}">
  {% csrf_field %}
  …
</form>
05

Stable storefront data

Global objects

Top-level objects are normalized by Jualify. Use the documented contract rather than implementation internals.

Store

shop

  • shop.nameStore name
  • shop.urlCanonical tenant URL
  • shop.logo_urlMerchant logo
  • shop.currency_codeBase currency
  • shop.timezoneIANA timezone
  • shop.currency_symbolDisplay symbol
Customization

settings and section

{{ settings.brand_color }}
{{ section.id }}
{{ section.settings.heading }}

{% for block in section.blocks %}
  {{ block.settings.title }}
{% endfor %}
Navigation & actions

routes

  • routes.root_urlHome
  • routes.search_urlProduct search
  • routes.cart_urlCart page
  • routes.checkout_urlCheckout display
  • routes.checkout_submit_urlCheckout POST
  • routes.account_urlCustomer account
Locale & menus

localization and menus.main

<html lang="{{ localization.language.code }}"
      dir="{% if localization.language.rtl %}rtl{% else %}ltr{% endif %}">

{% for link in menus.main %}
  <a href="{{ link.url }}" target="{{ link.target }}">
    {{ link.title }}
  </a>
{% endfor %}
Page context

template, request, theme

  • template.nameCurrent template handle
  • request.canonical_urlSEO-safe page URL
  • request.pathCurrent path
  • request.queryQuery values
  • theme.versionInstalled theme version
06

Catalog presentation

Products & discovery

Products, variants, collections, search, media, and pagination arrive as presentation-safe objects.

Product

Core product fields

  • product.titleLocalized title
  • product.slugStable SEO slug
  • product.urlCanonical supplied URL
  • product.priceActive numeric price
  • product.compare_at_priceOriginal sale price
  • product.availablePurchasable state
  • product.inventory_policydeny / continue
Media

Normalized image objects

{% assign image = product.featured_media %}
{% if image.url %}
  <img src="{{ image.url | image_url }}"
       alt="{{ image.alt | default: product.title }}"
       width="{{ image.width }}"
       height="{{ image.height }}">
{% endif %}
VariantsUse server URL

Variant matrix

{% unless product.has_only_default_variant %}
  {% for variant in product.variants %}
    <button data-url="{{ variant.add_to_cart_url }}"
            {% unless variant.available %}disabled{% endunless %}>
      {{ variant.title }} — {{ variant.price | money }}
    </button>
  {% endfor %}
{% endunless %}
Collections

collection

<h1>{{ collection.title }}</h1>
{% for product in products %}
  <a href="{{ product.url }}">{{ product.title }}</a>
{% endfor %}

Use collection.url; canonical collection URLs are /collections/{slug}.

Search

search

<form action="{{ routes.search_url }}" method="get">
  <input name="search" value="{{ search.term }}">
</form>

{% if search.performed %}
  {{ search.results_count }} results
{% endif %}
Pagination

Stable pagination object

{% if pagination.has_previous %}
  <a href="{{ pagination.previous_url }}">Previous</a>
{% endif %}
<span>{{ pagination.current_page }} / {{ pagination.last_page }}</span>
{% if pagination.has_next %}
  <a href="{{ pagination.next_url }}">Next</a>
{% endif %}
07

Server-owned transactions

Cart & checkout

Render supplied values and post to supplied endpoints. Never calculate or authorize transaction state in the theme.

Cart

Cart summary

{% if cart.empty %}
  <p>Your cart is empty.</p>
{% else %}
  {% for item in cart.items %}
    <a href="{{ item.url }}">{{ item.title }}</a>
    <span>{{ item.line_price | money }}</span>
  {% endfor %}
  <strong>{{ cart.total_price | money }}</strong>
{% endif %}
Cart mutation

Update quantities

<form method="post" action="{{ cart.update_url }}">
  {% csrf_field %}
  {% for item in cart.items %}
    <input type="number" name="qty[]"
           value="{{ item.quantity }}" min="0">
  {% endfor %}
  <button type="submit">Update cart</button>
</form>
CheckoutPresentation only

Checkout context

  • checkout.submit_urlSecure POST endpoint
  • customer_detailSafe customer summary
  • billing_addressBilling values
  • shipping_addressShipping values
  • shipping_methodsSelectable methods
  • payment_methodsCustomer-visible methods
  • checkout.total_priceDisplay-only total
Checkout POST

Keep checkout field names

<form method="post" action="{{ checkout.submit_url }}"
      enctype="multipart/form-data">
  {% csrf_field %}
  <input name="billing_fname" value="{{ billing_address.first_name }}">
  <select name="shipping_charge">…</select>
  <input type="radio" name="payment_method" value="cod">
  <button type="submit">Place order</button>
</form>

The compatibility shipping prefix is intentionally spelled shpping_*.

Customer

customer

{% if customer.logged_in %}
  <p>Hello, {{ customer.name }}</p>
  <a href="{{ customer.orders_url }}">
    Orders ({{ customer.orders_count }})
  </a>
{% else %}
  <a href="{{ routes.login_url }}">Sign in</a>
{% endif %}
Confirmation

order_success

{% if order_success.has_order %}
  <h1>Order {{ order_success.order_number }}</h1>
  <p>{{ order_success.payment_status }}</p>
  <p>{{ order_success.total | money }}</p>
  <a href="{{ order_success.order_url }}">View order</a>
{% endif %}
08

Merchant customization

Sections & schemas

Every section file ends with exactly one static JSON schema block. The schema drives validation and customizer controls.

Embedded schemaFinal content

One schema per section

<section>
  <h2>{{ section.settings.heading }}</h2>
</section>

{% schema %}
{
  "name": "Feature",
  "settings": [
    {"id": "heading", "type": "text", "label": "Heading", "default": "Featured"}
  ],
  "presets": [{"name": "Feature"}]
}
{% endschema %}
Controls

Supported setting types

texttextarearichtextcheckboxnumberrangeselectradiocolorurlimagecollectionproductpagemenuheaderparagraph
Blocks

Repeatable content blocks

{% for block in section.blocks %}
  <article data-block-id="{{ block.id }}">
    <h3>{{ block.settings.title }}</h3>
    <p>{{ block.settings.text }}</p>
  </article>
{% endfor %}

Define allowed block types and their settings in the same terminal schema object.

Validator rules

Schema guardrails

  • Exactly one block per section
  • Static, valid JSON only
  • Must be the file’s final content
  • Maximum 64 KiB
  • No adjacent *.schema.json file
  • IDs and types use safe handles
09

Publish with confidence

Safety & shipping

Compatibility, maintenance access, checkout boundaries, and a short preflight checklist.

CompatibilityAPI v1 only

Use documented API features

Use only the tags, filters, forms, blocks, and storefront objects documented by Jualify Liquid API v1. Undocumented helpers and integrations are unsupported.

Maintenance

storefront_maintenance

{% if storefront_maintenance.password_required %}
  <form method="post" action="{{ storefront_maintenance.unlock_url }}">
    {% csrf_field %}
    <input type="password" name="storefront_password" required>
    <button type="submit">Enter store</button>
  </form>
{% endif %}
Sensitive dataNever render

Private means server-only

  • Gateway credentials or secret keys
  • Payment tokens or full card data
  • Passwords or password hashes
  • Callback payloads or transaction internals
  • Arbitrary merchant tracking HTML
Preflight

Before distributing

  1. Validate every JSON and embedded schema.
  2. Keep theme.json at ZIP root.
  3. Upload and confirm the theme installs as a draft.
  4. Preview populated and empty template states.
  5. Test variants, sold-out, sale, and missing media.
  6. Test keyboard, mobile, reduced motion, and no-JS.
  7. Verify forms use server URLs and CSRF.
Keep nearby

Theme code is presentation.
Jualify stays the source of truth.

When in doubt, use the documented object, URL, or mutation endpoint rather than recreating application behavior in Liquid or JavaScript.

Back to quick start
Copied to clipboard