Templates
Build per-collection page templates that decide how pages, events, posts, and other records render
A template controls how a specific content type renders — how a page lays out, how an event presents its date and venue, how a post displays its author and body. Templates live in templates/ as either base templates like page.liquid or named alternates like page.landing.liquid.
For per-collection field references (the data shape of a page, event, post, etc.), see Template references.
Prerequisites
- A theme directory with the required structure.
- A working
layouts/default.liquid(see Layouts). - Familiarity with Liquid template syntax.
Steps
1. Create a template file
Templates use the .liquid extension and follow a strict naming convention:
templates/[collection].liquid
templates/[collection].[name].liquidThe collection is the segment before the first dot. The name is the second segment. If only one segment is provided, the name defaults to default.
| Filename | Collection | Name |
|---|---|---|
templates/page.liquid | page | default runtime template |
templates/page.landing.liquid | page | landing alternate |
templates/event.featured.liquid | event | featured alternate |
templates/blog.liquid | blog | default |
templates/post.archive.liquid | post | archive |
templates/person.liquid | person | default |
The filename — not the schema — sets the collection and the variant name. During upload, each template's collection and name are read straight from the filename and recorded in the theme. Editors then choose between the named variants (landing, featured, archive, and so on) when assigning a template to a record. Give files clear variant names, since that name is what editors see.
2. Write the template markup
Templates are HTML mixed with Liquid output tags and control flow. The current document is exposed as the variable matching the collection name. For a page template, use page:
<article class="page">
<header class="page__header">
<h1>{{ page.title }}</h1>
{% if page.description %}
<p class="page__description">{{ page.description }}</p>
{% endif %}
</header>
{% if page.image %}
<figure class="page__hero">
<img
src="{{ page.image | image_url: width: 1200 }}"
alt="{{ page.image.alt }}"
width="{{ page.image.width }}"
height="{{ page.image.height }}"
>
</figure>
{% endif %}
<div class="page__body">
{% stageblocks page %}
</div>
</article>Use {% render 'snippet-name' %} to include partials from snippets/.
{% render 'breadcrumbs', page: page %}
{% render 'social-sharing', title: page.title, url: request().url %}The {% include %} tag is deprecated. Use {% render %} instead.
3. Add a schema definition
Every template should include a {% schema %} tag with a JSON object. The schema defines settings (editable fields editors fill in per page) and blocks (which block types are allowed on this template).
Place the schema at the end of the file:
{% schema %}
{
"settings": [
{
"type": "checkbox",
"name": "show_breadcrumbs",
"label": "Show breadcrumbs",
"defaultValue": true
},
{
"type": "select",
"name": "header_style",
"label": "Header style",
"options": [
{ "label": "Standard", "value": "standard" },
{ "label": "Hero", "value": "hero" },
{ "label": "Minimal", "value": "minimal" }
],
"defaultValue": "standard"
}
],
"blocks": [
"text-section",
"image-gallery",
"call-to-action",
"featured-events"
]
}
{% endschema %}The settings array defines fields that appear in the editor. Each field requires type, name, and label; required and defaultValue are optional.
The blocks array lists allowed block types — strings matching the name property in each block's schema. Object form is also accepted:
"blocks": [
"text-section",
{ "name": "image-gallery" },
{ "type": "call-to-action" }
]4. Access template settings in markup
Template settings are available on the document via page.theme.settings:
{% assign show_breadcrumbs = page.theme.settings.show_breadcrumbs %}
{% if show_breadcrumbs %}
{% render 'breadcrumbs', page: page %}
{% endif %}
{% assign header_style = page.theme.settings.header_style %}
<header class="page__header page__header--{{ header_style }}">
<h1>{{ page.title }}</h1>
</header>Complete example
A full event.featured.liquid template for an arts venue:
{% assign show_series = event.series != blank %}
<article class="event event--featured">
<header class="event__header">
{% if event.image %}
<div class="event__hero">
<img
src="{{ event.image | image_url: width: 1600, height: 900, fit: 'cover' }}"
alt="{{ event.image.alt | default: event.title }}"
loading="eager"
>
</div>
{% endif %}
<div class="event__header-content">
{% if show_series %}
<a href="/series/{{ event.series.slug }}" class="event__series-link">
{{ event.series.title }}
</a>
{% endif %}
<h1 class="event__title">{{ event.title }}</h1>
{% if event.subtitle %}
<p class="event__subtitle">{{ event.subtitle }}</p>
{% endif %}
{% if event.venue %}
<p class="event__venue">
<a href="/venues/{{ event.venue.slug }}">{{ event.venue.title }}</a>
</p>
{% endif %}
</div>
</header>
<div class="event__body">
{% if event.description_html %}
<div class="event__description rich-text">
{{ event.description_html }}
</div>
{% endif %}
{% stageblocks event %}
</div>
{% render 'event-sidebar', event: event %}
</article>
{% schema %}
{
"settings": [
{
"type": "checkbox",
"name": "show_related_events",
"label": "Show related events",
"defaultValue": true
},
{
"type": "number",
"name": "related_events_count",
"label": "Number of related events to display",
"defaultValue": 4
}
],
"blocks": [
"text-section",
"image-gallery",
"video-embed",
"pull-quote",
"performer-list",
"booking-widget"
]
}
{% endschema %}This template renders when an editor assigns the featured template to an event in the admin.
Troubleshooting
"Template filename is not in the expected format" — The file must be a base template such as page.liquid or a named alternate such as page.landing.liquid. Ensure the filename has a collection segment and the .liquid extension.
"Invalid template schema JSON" — JSON syntax error inside {% schema %}. Validate it with a JSON linter. Common: trailing commas, unquoted keys, single quotes.
"Template references missing block" — A block name in the blocks array doesn't match any file in blocks/. Verify the block's name in its schema matches the string.
Rich text fields render as true instead of HTML — Rich text fields are split into two properties. Use field_name_html for the HTML; the plain field_name is a boolean.
Related
- Layouts — set up the wrapping layout for templates.
- Template context — variables available in templates.
- Blocks — define blocks referenced in template schemas.
- Rendering blocks — the
stageblockstag. - Block schema reference — field types for settings.
- Template references — per-collection data shape.