Template Reference
Every template a theme can provide, what each receives, and what happens when you leave one out.
How a Template Is Found
For any view the shop resolves in this order.
- The tenant's own copy of your theme, if the merchant has edited templates in their admin
- Your theme in the platform's theme directory
- The shared storefront fallback
A missing template is not an error
Anything you leave out falls back to the shared storefront version, which works but is unstyled to your theme. That is a reasonable way to ship a first version. It is also why a merchant reports a page as broken rather than missing: to them it is simply the page that does not match.
The Templates
| Template | Shows |
|---|---|
home.twig |
The homepage |
layouts/app.twig |
Wraps nearly everything |
layouts/checkout.twig |
Wraps the checkout only |
shop/index.twig |
Listings, categories, search results |
shop/show.twig |
A product |
shop/brands.twig |
All brands |
shop/brand-show.twig |
One brand |
shop/tag.twig |
One tag |
shop/compare.twig |
Product comparison |
blog/index.twig |
Blog listing |
blog/show.twig |
A post |
blog/category.twig |
A blog category |
pages/default.twig |
A content page |
pages/landing.twig |
A landing page |
pages/contact.twig |
Contact, with its form |
pages/faq.twig |
FAQ |
pages/sections.twig |
A page the merchant has built from sections. Optional |
blog/_list-list.twig |
The blog listing as rows. Optional, and the fallback |
blog/_list-grid.twig |
The blog listing as tiles. Optional |
Build in this order, by how much each is seen: shop/show.twig, shop/index.twig, home.twig, then the two layouts.
Blog Layouts
The merchant chooses how the blog lists articles in Theme Settings, Blog Listing, and your theme decides which choices exist. Declare them in the manifest:
{
"key": "blog_layout",
"type": "select",
"label": "Article Layout",
"default": "list",
"options": { "list": "List", "grid": "Tiles", "magazine": "Magazine" }
}
Then ship blog/_list-<value>.twig for each value you offered. The listing includes the one the setting names, passing it posts, and your partial renders them however it likes.
Offer a value and forget the file and the page falls back to blog/_list-list.twig rather than drawing an empty column, so a half finished theme still shows the writing. That makes the list layout the one to build first.
theme_has('blog/_list-grid.twig') is how that check is made, and it is available to you for the same purpose anywhere else.
Both the blog index and a category page draw the same shell, so a layout you supply works on both without any extra work.
Two Layouts, on Purpose
layouts/app.twig carries your header, navigation, footer and anything global.
layouts/checkout.twig should carry almost none of it. A checkout with full navigation, a search box and a newsletter signup gives a shopper a dozen ways to leave at the moment you least want them to.
What Each Template Receives
shop/index.twig
| Variable | Is |
|---|---|
products |
The paginated results |
categories |
The category tree for navigation |
currentCategory |
The category being viewed, or null |
subcategories |
Children of the current category |
activeRootCategoryId |
Which top level section is active |
brands |
Brands with visible products |
tags |
Available tags |
activeFilterCount |
How many filters are applied |
breadcrumbs |
The trail |
seoTitle, seoDescription, seoCanonical |
For the head |
products is a paginator, so products.links, products.total and products.currentPage all work.
currentCategory is null on the main listing
shop/index.twig serves both the whole catalogue and a single category. Guard every use of currentCategory, or the catalogue page breaks while the category pages look fine.
shop/show.twig
| Variable | Is |
|---|---|
product |
The product, with variants, categories and media loaded |
relatedProducts |
Suggestions |
recentlyViewed |
This shopper's recent products |
seoTitle, seoDescription, seoCanonical |
For the head |
blog/index.twig and blog/category.twig
| Variable | Is |
|---|---|
posts |
The paginated articles |
categories |
Blog categories, each with posts_count |
category |
The category being viewed. Only on blog/category.twig |
breadcrumbs |
The trail |
seoTitle, seoDescription |
For the head |
How many articles a page holds is the merchant's Articles Per Page setting, so do not assume twelve.
blog/show.twig
| Variable | Is |
|---|---|
post |
The article, with its categories |
relatedPosts |
Articles sharing a category, as many as the merchant asked for, empty when they asked for none |
previousPost, nextPost |
The articles either side in reading order, newest first. Null at each end, and null when the merchant has switched the links off |
breadcrumbs |
The trail, including the article's first category when it has one |
seoTitle, seoDescription, seoImage |
For the head |
Everything a merchant can switch off here is a Theme Settings, Blog Post value, read through themeConfig. Guard each one rather than assuming it is on.
Everywhere
themeConfig holds your theme's settings. The rest of the shop is reached through functions rather than variables, so that a template does not depend on a controller having passed something.
Shared Components
Templates under components/storefront/ are shared across every theme.
{% include 'components/storefront/product-badges.twig' with { product: product } %}
{% include 'components/storefront/breadcrumbs.twig' with { breadcrumbs: breadcrumbs } %}
{% include 'components/storefront/social-icon.twig' with { platform: link.platform } %}
Include rather than reimplement. When the platform adds a pre-order badge or a new social network, your theme gets it without a release.
Content Pages and Templates
The templates array in your manifest lists which page layouts a merchant can choose, and each must have a matching file in pages/.
A theme can also ship the pages themselves, declared under pages in the manifest, so a new shop starts with an About and a Contact page rather than empty navigation. Each one carries either HTML in content/ or a section layout. See Shipping Your Pages and Your Homepage.
Pages Built From Sections
A merchant can build a page from sections instead of writing its body, if your theme offers that. It does so by declaring sections with "templates": ["page"] and shipping pages/sections.twig. See Sections.
The controller picks the template:
| The page | Template used |
|---|---|
| No layout, which is every page until a merchant builds one | pages/{its template}.twig |
| A layout holding sections your theme declares | pages/sections.twig |
| A layout holding only sections your theme does not declare | pages/{its template}.twig |
That last row is why a layout built under another theme leaves a shorter page rather than a blank one.
pages/sections.twig receives sections, in the merchant's order, each with type, settings and a resolved partial. It also receives everything a page template normally gets, including page and breadcrumbs.
A built page has no body to draw
A page is either written or built. Once it has a layout the merchant's Content tab is switched off and page.content is empty, so a section that prints it draws nothing and a theme that relies on it has no words on the page at all. Words on a built page come from a text section you provide.
A theme that wants nothing to do with this declares no page sections and ships its own pages/*.twig. That is a complete and supported way to build a theme, and the merchant's Page Builder tab says as much.
Pagination
Use the paginator's own output rather than building links by hand, so query parameters such as the current sort and filters are carried through.
A listing that loses the active filter on page two is the most common pagination fault in a submitted theme.
