Shopify Architecture
Before you write Liquid code or build sections, you need a clear mental model of how Shopify renders pages. This chapter explains the entire rendering pipeline — from a customer clicking a URL to the final HTML appearing in their browser.
Time estimate: 30 – 45 minutes
Tags:
Liquid
Architecture
The Rendering Pipeline
When a customer visits a Shopify store, an intricate rendering pipeline executes in milliseconds. Understanding this pipeline is the foundation of professional theme development.
Customer visits: https://your-store.com/products/awesome-shirt
┌─────────────────────────────────────────────────────┐
│ 1. URL ROUTING │
│ Shopify matches the URL to a resource type: │
│ /products/awesome-shirt → product page │
│ /collections/summer → collection page │
│ /pages/about → custom page │
│ / → home page │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. TEMPLATE SELECTION │
│ Shopify looks for a JSON template: │
│ templates/product.json (default) │
│ templates/product.alternate.json (alternate) │
│ The JSON template lists which SECTIONS to load. │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. DATA LOADING │
│ Shopify loads the relevant data objects: │
│ • product (the shirt's data) │
│ • shop (store settings) │
│ • settings (theme settings) │
│ • request (locale, host, etc.) │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. SECTION RENDERING │
│ Each section listed in the JSON template │
│ is rendered: │
│ sections/main-product.liquid → HTML │
│ sections/product-recommendations.liquid → HTML │
│ Sections can include SNIPPETS for reuse. │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. LAYOUT WRAPPING │
│ All rendered sections are injected into │
│ the LAYOUT file (layout/theme.liquid): │
│ <html> │
│ <head>...</head> │
│ <body> │
│ {% sections 'header-group' %} │
│ {{ content_for_layout }} ← sections go here │
│ {% sections 'footer-group' %} │
│ </body> │
│ </html> │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 6. FINAL HTML │
│ Complete HTML document sent to customer's browser │
│ CSS & JS assets loaded │
│ Page renders visually │
└─────────────────────────────────────────────────────┘
This pipeline runs on every single page request. As a theme developer, you control steps 2 through 5 — the templates, sections, snippets, and layout.
The Four Layers of a Theme
Think of a Shopify theme as four concentric layers, each wrapping the next:
Layer 1: Layout
The layout is the outermost shell. It contains everything that appears
on every page: the <html> tag, <head>,
global CSS/JS, the header, and the footer.
<!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 }} — {{ shop.name }}</title>
{{ 'base.css' | asset_url | stylesheet_tag }}
{{ content_for_header }}
</head>
<body>
{%- sections 'header-group' -%}
<main id="MainContent" role="main">
{{ content_for_layout }}
</main>
{%- sections 'footer-group' -%}
<script src="{{ 'global.js' | asset_url }}" defer></script>
</body>
</html>
Key elements:
{{ content_for_header }}— Required. Shopify injects analytics scripts, meta tags, and app scripts here. Never remove this.{{ content_for_layout }}— Required. This is where all the page-specific content (rendered sections) appears.{%- sections 'header-group' -%}— Renders the header section group (global across all pages).{%- sections 'footer-group' -%}— Renders the footer section group.
content_for_header
Removing {{ content_for_header }} will break Shopify analytics, app
functionality, and potentially payment processing. It must always be inside the
<head> tag of your layout.
Layer 2: Template
The template determines which sections appear on a specific page type. In Online Store 2.0, templates are JSON files that reference sections.
{
"sections": {
"main": {
"type": "main-product",
"settings": {
"show_vendor": true,
"show_sku": false
},
"blocks": {
"title": { "type": "title" },
"price": { "type": "price" },
"variant_picker": { "type": "variant_picker" },
"buy_buttons": { "type": "buy_buttons" },
"description": { "type": "description" }
},
"block_order": ["title", "price", "variant_picker", "buy_buttons", "description"]
},
"recommendations": {
"type": "product-recommendations",
"settings": {
"heading": "You may also like"
}
}
},
"order": ["main", "recommendations"]
}
Key things to understand about JSON templates:
"sections"— An object containing all sections on this page"type"— References a file insections/(e.g.,"main-product"maps tosections/main-product.liquid)"settings"— Pre-configured values for the section's schema settings"blocks"— Sub-components within the section"order"— The display order of sections on the page
JSON templates are what make Shopify themes modular. Merchants can add, remove, and reorder sections on any page using the theme editor — without touching code. This is a massive selling point for premium themes.
Layer 3: Sections
Sections are the building blocks of every page. Each section is a self-contained module with its own:
- Liquid markup (HTML structure)
- CSS styles (via
{% style %}or{% stylesheet %}) - JavaScript (via
{% javascript %}or external files) - Schema (JSON configuration for the theme editor)
<section class="featured-text section-{{ section.id }}">
<div class="page-width">
{%- if section.settings.heading != blank -%}
<h2>{{ section.settings.heading }}</h2>
{%- endif -%}
{%- if section.settings.text != blank -%}
<div class="rte">
{{ section.settings.text }}
</div>
{%- endif -%}
</div>
</section>
{% schema %}
{
"name": "Featured Text",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Talk about your brand"
},
{
"type": "richtext",
"id": "text",
"label": "Text",
"default": "<p>Share your brand story with customers.</p>"
}
],
"presets": [
{
"name": "Featured Text"
}
]
}
{% endschema %}
We'll dive deep into sections in the Sections & Blocks chapter.
Layer 4: Snippets
Snippets are reusable code fragments. They don't have schemas — they're pure Liquid/HTML that you include from sections or templates.
{%- comment -%}
Renders a product card.
Accepts:
- product: {Object} Product Liquid object
- show_vendor: {Boolean} Show vendor name
Usage:
{% render 'product-card', product: product, show_vendor: true %}
{%- endcomment -%}
<div class="product-card">
<a href="{{ product.url }}" class="product-card__link">
<div class="product-card__image">
{%- if product.featured_image -%}
<img
src="{{ product.featured_image | image_url: width: 400 }}"
alt="{{ product.featured_image.alt | escape }}"
width="400"
height="{{ 400 | divided_by: product.featured_image.aspect_ratio | round }}"
loading="lazy"
>
{%- endif -%}
</div>
<div class="product-card__info">
{%- if show_vendor -%}
<span class="product-card__vendor">{{ product.vendor }}</span>
{%- endif -%}
<h3 class="product-card__title">{{ product.title }}</h3>
<span class="product-card__price">{{ product.price | money }}</span>
</div>
</a>
</div>
The key difference between sections and snippets:
| Feature | Section | Snippet |
|---|---|---|
| Has schema (theme editor settings) | ✓ Yes | ✗ No |
| Can be added/removed by merchants | ✓ Yes | ✗ No |
| Can contain blocks | ✓ Yes | ✗ No |
| Reusable across multiple sections | ✗ No (unique per page) | ✓ Yes |
| Included with | JSON template or section groups | {% render 'snippet-name' %} |
| Has its own scope | ✓ Yes (section object) |
✓ Yes (isolated scope via render) |
Use sections for top-level page components that merchants should be able to customize (hero banners, featured collections, testimonials). Use snippets for reusable UI elements that appear inside sections (product cards, icons, price displays).
Page Types and URL Routing
Shopify automatically routes URLs to specific template types. You don't configure routing — it's handled by the platform. Understanding this mapping is essential:
| URL Pattern | Template File | Main Data Object |
|---|---|---|
/ |
templates/index.json |
shop |
/products/handle |
templates/product.json |
product |
/collections/handle |
templates/collection.json |
collection |
/collections |
templates/list-collections.json |
collections |
/pages/handle |
templates/page.json |
page |
/blogs/handle |
templates/blog.json |
blog |
/blogs/handle/article |
templates/article.json |
article |
/cart |
templates/cart.json |
cart |
/search?q=... |
templates/search.json |
search |
/account |
templates/customers/account.liquid |
customer |
/account/login |
templates/customers/login.liquid |
— |
| 404 errors | templates/404.json |
— |
You can create alternate templates for any page type by adding a
suffix. For example, templates/product.special.json creates an alternate
product template called "special" that merchants can assign to specific products. This
is incredibly powerful for stores with different product types (e.g., a clothing product
vs. a digital download).
Section Groups
Section groups are collections of sections that appear on every page — regardless of which template is being rendered. The most common section groups are the header and footer.
{
"type": "header",
"name": "Header group",
"sections": {
"announcement-bar": {
"type": "announcement-bar",
"settings": {
"text": "Free shipping on orders over $50"
}
},
"header": {
"type": "header",
"settings": {
"logo_position": "middle-center",
"menu": "main-menu",
"sticky_header_type": "on-scroll-up"
}
}
},
"order": ["announcement-bar", "header"]
}
In the layout file, section groups are rendered with:
{%- sections 'header-group' -%}
{{ content_for_layout }}
{%- sections 'footer-group' -%}
How Data Flows Through a Theme
Understanding data flow is critical. Here's how data moves from Shopify's database to your Liquid templates:
┌──────────────────────────────────────┐
│ SHOPIFY DATABASE │
│ │
│ Products, Collections, Pages, │
│ Customers, Orders, Blog Articles, │
│ Navigation Menus, Shop Settings │
└───────────────┬──────────────────────┘
│
│ Shopify loads relevant data
│ based on the page type
▼
┌──────────────────────────────────────┐
│ GLOBAL OBJECTS │
│ │
│ Always available on every page: │
│ • shop (store info) │
│ • settings (theme settings) │
│ • request (locale, host) │
│ • cart (current cart) │
│ • linklists (nav menus) │
│ • pages (all pages) │
│ • collections (all collections) │
│ • canonical_url │
│ • page_title │
│ • content_for_header │
└───────────────┬──────────────────────┘
│
│ + page-specific objects
▼
┌──────────────────────────────────────┐
│ PAGE-SPECIFIC OBJECTS │
│ │
│ Product page: product │
│ Collection: collection │
│ Cart page: cart (with items) │
│ Blog page: blog, articles │
│ Search page: search │
└───────────────┬──────────────────────┘
│
│ + section-level data
▼
┌──────────────────────────────────────┐
│ SECTION OBJECTS │
│ │
│ section.id │
│ section.settings │
│ section.blocks │
└───────────────┬──────────────────────┘
│
│ Access via Liquid tags
▼
┌──────────────────────────────────────┐
│ LIQUID TEMPLATE │
│ │
│ {{ product.title }} │
│ {{ section.settings.heading }} │
│ {% for block in section.blocks %} │
│ {{ shop.name }} │
│ {{ cart.item_count }} │
└──────────────────────────────────────┘
Theme Settings Architecture
Settings in Shopify themes exist at two levels:
Global Theme Settings
Defined in config/settings_schema.json, these settings affect the
entire theme — colors, typography, social links, favicon, etc.
[
{
"name": "theme_info",
"theme_name": "My Theme",
"theme_version": "1.0.0",
"theme_author": "Gamal",
"theme_documentation_url": "https://example.com",
"theme_support_url": "https://example.com/support"
},
{
"name": "Colors",
"settings": [
{
"type": "color",
"id": "color_primary",
"label": "Primary color",
"default": "#6366f1"
},
{
"type": "color",
"id": "color_secondary",
"label": "Secondary color",
"default": "#8b5cf6"
},
{
"type": "color",
"id": "color_background",
"label": "Background color",
"default": "#ffffff"
},
{
"type": "color",
"id": "color_text",
"label": "Text color",
"default": "#1a1a2e"
}
]
},
{
"name": "Typography",
"settings": [
{
"type": "font_picker",
"id": "font_heading",
"label": "Heading font",
"default": "helvetica_n7"
},
{
"type": "font_picker",
"id": "font_body",
"label": "Body font",
"default": "helvetica_n4"
}
]
}
]
Global settings are accessed in Liquid with {{ settings.color_primary }}.
Section-Level Settings
Each section has its own settings defined in its {% schema %} block.
These settings are accessed with {{ section.settings.setting_id }}.
Settings Data Storage
When a merchant customizes settings through the theme editor, the values are saved in
config/settings_data.json. You should never edit this file manually.
It's auto-generated by the theme editor.
The config/settings_data.json file contains all the merchant's customizations.
When you pull a theme with shopify theme pull, this file comes with it.
Be careful not to overwrite a merchant's settings when pushing changes. Always communicate
with your client about deployment procedures.
The Asset Pipeline
All static files (CSS, JavaScript, images, fonts) live in the assets/
directory. Shopify serves them through its CDN (Content Delivery Network) for fast
global delivery.
Referencing Assets in Liquid
<!-- CSS file -->
{{ 'base.css' | asset_url | stylesheet_tag }}
<!-- JavaScript file -->
<script src="{{ 'global.js' | asset_url }}" defer></script>
<!-- Image -->
<img src="{{ 'logo.png' | asset_url }}" alt="{{ shop.name }}">
<!-- Font file -->
@font-face {
font-family: 'CustomFont';
src: url('{{ "custom-font.woff2" | asset_url }}') format('woff2');
}
The asset_url filter converts a filename into a full CDN URL. Shopify
handles caching, compression, and global distribution automatically.
CSS in Shopify Themes
Shopify themes don't use build tools like Webpack or Vite by default. CSS is written
in plain .css files in the assets/ folder. You can also use
CSS custom properties (variables) tied to theme settings:
{% style %}
:root {
--color-primary: {{ settings.color_primary }};
--color-secondary: {{ settings.color_secondary }};
--color-background: {{ settings.color_background }};
--color-text: {{ settings.color_text }};
--font-heading: {{ settings.font_heading.family }}, {{ settings.font_heading.fallback_families }};
--font-body: {{ settings.font_body.family }}, {{ settings.font_body.fallback_families }};
}
{% endstyle %}
This pattern bridges theme editor settings with your CSS. When a merchant changes the
primary color, every element using var(--color-primary) updates automatically.
Define all customizable colors and fonts as CSS custom properties in the layout file. Then reference those variables throughout your CSS. This creates a clean separation between theme settings and styles, and makes global changes effortless.
The Complete Request Lifecycle
Let's trace a complete request from start to finish. A customer visits
your-store.com/collections/summer:
DNS Resolution
The browser resolves your-store.com to Shopify's servers.
Shopify handles all DNS, SSL, and server infrastructure.
Route Matching
Shopify recognizes /collections/summer as a collection page.
It loads the summer collection from the database.
Template Selection
Shopify checks if this collection has an assigned alternate template. If not,
it uses templates/collection.json.
Data Preparation
Shopify prepares the collection object with all its products,
pagination info, and metadata. It also prepares global objects (shop,
settings, cart, etc.).
Section Rendering
Each section listed in collection.json is rendered. The Liquid
engine processes all {{ }} outputs and {% %} logic.
Snippets referenced by {% render %} are included.
Layout Assembly
The rendered sections are placed into {{ content_for_layout }}
inside layout/theme.liquid. The header and footer section groups
are rendered and placed.
HTML Delivery
The complete HTML document is sent to the browser. CSS and JS files are loaded from Shopify's CDN. The page renders.
Online Store 2.0 vs. 1.0
You may encounter older themes or tutorials based on Online Store 1.0. Here are the key differences:
| Feature | OS 1.0 (Legacy) | OS 2.0 (Current) |
|---|---|---|
| Templates | Liquid files (.liquid) |
JSON files (.json) |
| Sections on homepage | ✓ | ✓ |
| Sections on all pages | ✗ | ✓ |
| App blocks | ✗ | ✓ |
| Metafield access | Limited | Full support |
| Section groups | ✗ | ✓ |
| Alternate templates | Liquid-based | JSON-based (easier to manage) |
Always build for Online Store 2.0. OS 1.0 themes are legacy and
will eventually lose support for new features. If you encounter a tutorial that
shows Liquid templates (not JSON) or uses {{ content_for_index }},
it's an OS 1.0 pattern — skip it and find a modern resource.
Your Mental Model
Here's the mental model I want you to carry through the rest of this handbook:
A Shopify theme is a collection of Liquid files organized into layouts, templates, sections, and snippets. Templates are JSON files that reference sections. Sections are modular components with configurable settings. The layout wraps everything in a consistent HTML shell with global elements like the header and footer.
— Your guiding mental model
Memorize this hierarchy:
Layout (theme.liquid)
└── Section Groups (header, footer)
└── Template (JSON)
└── Sections (Liquid components)
└── Blocks (sub-components)
└── Snippets (reusable partials)
Key Takeaways
- Shopify handles servers, databases, security — you handle front-end presentation
- The rendering pipeline: URL → Template → Sections → Layout → HTML
- Layouts wrap everything; templates define page structure; sections are modular components
- Snippets are reusable code fragments without schemas
- JSON templates make themes modular and merchant-customizable
- Global objects (
shop,settings,cart) are available everywhere - Page-specific objects (
product,collection) depend on the page type - CSS custom properties bridge theme settings and stylesheets
- Always build for Online Store 2.0
In the next chapter, we'll learn the Liquid templating language — the tool that lets you access all this data and build dynamic, data-driven templates.