UnifyCart Developers

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.

  1. The tenant's own copy of your theme, if the merchant has edited templates in their admin
  2. Your theme in the platform's theme directory
  3. 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.