10 KiB
10 KiB
Themes Reference
Guide for developing Shopify themes with Liquid templating.
Liquid Templating
Syntax Basics
Objects (Output):
{{ product.title }}
{{ product.price | money }}
{{ customer.email }}
Tags (Logic):
{% if product.available %}
<button>Add to Cart</button>
{% else %}
<p>Sold Out</p>
{% endif %}
{% for product in collection.products %}
{{ product.title }}
{% endfor %}
{% case product.type %}
{% when 'Clothing' %}
<span>Apparel</span>
{% when 'Shoes' %}
<span>Footwear</span>
{% else %}
<span>Other</span>
{% endcase %}
Filters (Transform):
{{ product.title | upcase }}
{{ product.price | money }}
{{ product.description | strip_html | truncate: 100 }}
{{ product.image | img_url: 'medium' }}
{{ 'now' | date: '%B %d, %Y' }}
Common Objects
Product:
{{ product.id }}
{{ product.title }}
{{ product.handle }}
{{ product.description }}
{{ product.price }}
{{ product.compare_at_price }}
{{ product.available }}
{{ product.type }}
{{ product.vendor }}
{{ product.tags }}
{{ product.images }}
{{ product.variants }}
{{ product.featured_image }}
{{ product.url }}
Collection:
{{ collection.title }}
{{ collection.handle }}
{{ collection.description }}
{{ collection.products }}
{{ collection.products_count }}
{{ collection.image }}
{{ collection.url }}
Cart:
{{ cart.item_count }}
{{ cart.total_price }}
{{ cart.items }}
{{ cart.note }}
{{ cart.attributes }}
Customer:
{{ customer.email }}
{{ customer.first_name }}
{{ customer.last_name }}
{{ customer.orders_count }}
{{ customer.total_spent }}
{{ customer.addresses }}
{{ customer.default_address }}
Shop:
{{ shop.name }}
{{ shop.email }}
{{ shop.domain }}
{{ shop.currency }}
{{ shop.money_format }}
{{ shop.enabled_payment_types }}
Common Filters
String:
upcase,downcase,capitalizestrip_html,strip_newlinestruncate: 100,truncatewords: 20replace: 'old', 'new'
Number:
money- Format currencyround,ceil,floortimes,divided_by,plus,minus
Array:
join: ', 'first,lastsizemap: 'property'where: 'property', 'value'
URL:
img_url: 'size'- Image URLurl_for_type,url_for_vendorlink_to,link_to_type
Date:
date: '%B %d, %Y'
Theme Architecture
Directory Structure
theme/
├── assets/ # CSS, JS, images
├── config/ # Theme settings
│ ├── settings_schema.json
│ └── settings_data.json
├── layout/ # Base templates
│ └── theme.liquid
├── locales/ # Translations
│ └── en.default.json
├── sections/ # Reusable blocks
│ ├── header.liquid
│ ├── footer.liquid
│ └── product-grid.liquid
├── snippets/ # Small components
│ ├── product-card.liquid
│ └── icon.liquid
└── templates/ # Page templates
├── index.json
├── product.json
├── collection.json
└── cart.liquid
Layout
Base template wrapping all pages (layout/theme.liquid):
<!DOCTYPE html>
<html lang="{{ request.locale.iso_code }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{ page_title }}</title>
{{ content_for_header }}
<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}">
</head>
<body>
{% section 'header' %}
<main>
{{ content_for_layout }}
</main>
{% section 'footer' %}
<script src="{{ 'theme.js' | asset_url }}"></script>
</body>
</html>
Templates
Page-specific structures (templates/product.json):
{
"sections": {
"main": {
"type": "product-template",
"settings": {
"show_vendor": true,
"show_quantity_selector": true
}
},
"recommendations": {
"type": "product-recommendations"
}
},
"order": ["main", "recommendations"]
}
Legacy format (templates/product.liquid):
<div class="product">
<div class="product-images">
<img src="{{ product.featured_image | img_url: 'large' }}" alt="{{ product.title }}">
</div>
<div class="product-details">
<h1>{{ product.title }}</h1>
<p class="price">{{ product.price | money }}</p>
{% form 'product', product %}
<select name="id">
{% for variant in product.variants %}
<option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}
</select>
<button type="submit">Add to Cart</button>
{% endform %}
</div>
</div>
Sections
Reusable content blocks (sections/product-grid.liquid):
<div class="product-grid">
{% for product in section.settings.collection.products %}
<div class="product-card">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</a>
</div>
{% endfor %}
</div>
{% schema %}
{
"name": "Product Grid",
"settings": [
{
"type": "collection",
"id": "collection",
"label": "Collection"
},
{
"type": "range",
"id": "products_per_row",
"min": 2,
"max": 5,
"step": 1,
"default": 4,
"label": "Products per row"
}
],
"presets": [
{
"name": "Product Grid"
}
]
}
{% endschema %}
Snippets
Small reusable components (snippets/product-card.liquid):
<div class="product-card">
<a href="{{ product.url }}">
{% if product.featured_image %}
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
{% endif %}
<h3>{{ product.title }}</h3>
<p class="price">{{ product.price | money }}</p>
{% if product.compare_at_price > product.price %}
<p class="sale-price">{{ product.compare_at_price | money }}</p>
{% endif %}
</a>
</div>
Include snippet:
{% render 'product-card', product: product %}
Development Workflow
Setup
# Initialize new theme
shopify theme init
# Choose Dawn (reference theme) or blank
Local Development
# Start local server
shopify theme dev
# Preview at http://localhost:9292
# Changes auto-sync to development theme
Pull Theme
# Pull live theme
shopify theme pull --live
# Pull specific theme
shopify theme pull --theme=123456789
# Pull only templates
shopify theme pull --only=templates
Push Theme
# Push to development theme
shopify theme push --development
# Create new unpublished theme
shopify theme push --unpublished
# Push specific files
shopify theme push --only=sections,snippets
Theme Check
Lint theme code:
shopify theme check
shopify theme check --auto-correct
Common Patterns
Product Form with Variants
{% form 'product', product %}
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
<div class="product-option">
<label>{{ option.name }}</label>
<select name="options[{{ option.name }}]">
{% for value in option.values %}
<option value="{{ value }}">{{ value }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
{% endunless %}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
<input type="number" name="quantity" value="1" min="1">
<button type="submit" {% unless product.available %}disabled{% endunless %}>
{% if product.available %}Add to Cart{% else %}Sold Out{% endif %}
</button>
{% endform %}
Pagination
{% paginate collection.products by 12 %}
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
{% if paginate.pages > 1 %}
<div class="pagination">
{% if paginate.previous %}
<a href="{{ paginate.previous.url }}">Previous</a>
{% endif %}
{% for part in paginate.parts %}
{% if part.is_link %}
<a href="{{ part.url }}">{{ part.title }}</a>
{% else %}
<span class="current">{{ part.title }}</span>
{% endif %}
{% endfor %}
{% if paginate.next %}
<a href="{{ paginate.next.url }}">Next</a>
{% endif %}
</div>
{% endif %}
{% endpaginate %}
Cart AJAX
// Add to cart
fetch('/cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: variantId,
quantity: 1
})
})
.then(res => res.json())
.then(item => console.log('Added:', item));
// Get cart
fetch('/cart.js')
.then(res => res.json())
.then(cart => console.log('Cart:', cart));
// Update cart
fetch('/cart/change.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: lineItemKey,
quantity: 2
})
})
.then(res => res.json());
Metafields in Themes
Access custom data:
{{ product.metafields.custom.care_instructions }}
{{ product.metafields.custom.material.value }}
{% if product.metafields.custom.featured %}
<span class="badge">Featured</span>
{% endif %}
Best Practices
Performance:
- Optimize images (use appropriate sizes)
- Minimize Liquid logic complexity
- Use lazy loading for images
- Defer non-critical JavaScript
Accessibility:
- Use semantic HTML
- Include alt text for images
- Support keyboard navigation
- Ensure sufficient color contrast
SEO:
- Use descriptive page titles
- Include meta descriptions
- Structure content with headings
- Implement schema markup
Code Quality:
- Follow Shopify theme guidelines
- Use consistent naming conventions
- Comment complex logic
- Keep sections focused and reusable
Resources
- Theme Development: https://shopify.dev/docs/themes
- Liquid Reference: https://shopify.dev/docs/api/liquid
- Dawn Theme: https://github.com/Shopify/dawn
- Theme Check: https://shopify.dev/docs/themes/tools/theme-check