Short recipes for conditional layout, entity-driven pages, lists, pagination, and structured field content.
Many templates reuse a small set of recipes. Each section below is one pattern, with the examples that belong to that pattern.
For an end-to-end walkthrough of fetching, filtering, looping, and paginating CMS collections, see Creating and Using Collections. Complete page templates, including a generic page and a pagination partial, live under Complete Examples.
Structuring repeatable field content Fieldsets let editors manage a consistent group of related values as one structured unit. A fieldset can hold one group or repeat the same group as many times as needed, so a template does not have to hard-code a fixed number of entries.
Choosing the model Use a fieldset when values belong together inside one owning entity. Use a scalar field for one independent value. Use CMS entities or datastore items when each item needs its own identity, URL, querying, governance, or reuse beyond the owner. Do not reach for a fieldset when content needs any of those.
When a fieldset's shape is unfamiliar, use the inspect filter during development. Keep the inspection depth bounded and replace inspection output with intentional child-field rendering before production. For how these values read at runtime, see Field Values.
Conditional Layout
Show or hide a region with if or unless, often from a query parameter, toggle, or login state.
Show or hide a section based on a query parameter, a toggle, or a condition. For example, include a sidebar only when a query parameter is present, or show different content when the user is logged in.
Use {% if %} or {% unless %} around the block. If the block is large, pull it into an include so the main template stays readable. Pass in only the data the partial needs.
The same idea applies to personalization checks (logged-in vs guest, client session counts) and to any optional chrome that should not render empty markup when the condition is false.
ExampleInclude sidebar template unless a query param is falseInclude a sidebar template only when a query parameter is not false, using unless.
ExampleCount client sessions within a time window (e.g. one year)Count how many sessions the client has had within a time window (e.g. one year) for analytics or limits.
Liquid
{%- if client.allowed and client.num_sessions > 6 and client.VIP != "true" and client.first_request_date < request.date | add_years: -1 -%}
<p>You have viewed this site {{ client.num_sessions }} since {{ client.first_request_date | date: 'MMMM yyyy' }}. During that time you have looked at {{ client.num_pages }} pages. We are happy that you have taken so much interest in us, and would like to invite you to <a href="#"> join our VIP club</a>!</p>
{%- endif -%}
Entity-Driven Content
Root entity is always set; guard renamed copies you pass into partials, not entity itself.
Every page has entity on the root scope. It is a reserved variable name, so templates cannot replace it. You do not need to wrap {{ entity }}, {{ entity.title }}, {{ entity.has_url }}, or other root entity properties in {% if entity is_valid %}. That test is always true, so the guard only adds a branch that never runs. Output them directly. Branch on a property only when that property itself is optional (for example an optional custom field).
The guard belongs on other variables. That is the usual case when you pass entity into a partial under a different name:
{% include '/sections/item_content.liquid' item:entity %}
Inside that section, item is a local input, not the reserved root entity. Guard it before you output:
{% if item.is_valid %} … {% endif %}
When you need to branch by content type on the current page, use entity.object_type (see the linked example). Optional fields on entity still need is_valid or default.
For what an entity is and which type-specific properties it has, see Entity under Content. The Entity Object and Entity-Driven Pages is the short howto for using entity on a page versus in a partial.
ExampleOutput the type of an unknown variableUse the object_type filter to get the type of an unknown property or variable.
Liquid
{%- blog_post post = "post" -%}
{{- post | object_type -}}
{{- post | object_type: true }}
Output
blog_post
object
ExampleUse a post_date or date-time field with the date filterUse a post_date or other date-time field and format or compare it with the date filter and date properties.
Liquid
{%- if entity.rehearsal_start.is_valid -%}
<p>Rehearsal will begin on {{ entity.rehearsal_start | date: "MMMM dd, yyyy 'at' h:m t" }} UTC</p>
{%- endif -%}
Lists and Limits
Take a slice of a list, pick first or last items, and loop without dumping the whole collection.
To show the first N items or a window of a list, use the limit or slice filter before looping: {% for item in items | limit: 10 %}. Combine with where and sort to filter and order the list before iteration. first, last, and square-bracket index access pick a single item when you do not need a loop.
Check that the list is usable before you emit wrappers, so you do not render an empty list or broken markup.
These recipes assume you already have a list in scope. For fetching CMS collections, narrowing them, and looping end to end, see Creating and Using Collections.
ExampleGetting a specific item from a listUse the first and last filter to get a specific item in a list. In some cases, you can use square bracket notation to get an item at a specific index.
Liquid
{% var input = 'a,b,c,d' | split:',' %}
first
Liquid
{{input | first}}
Output
a
last
Liquid
{{input | last}}
Output
d
square bracket
Liquid
{{input[2]}}
Output
c
Liquid
[{{input[5]}}]
Output
[]
Liquid
{{input[-1]}}
Output
d
ExampleGenerate a list of visited pages. If the page response code isn't successful then return the response codeBuild a list of visited pages (e.g. from session) and return the response code when the page is not successful.
Liquid
<h4>Recent Requests</h4>
<ol>
{%- for pageview in session.history limit:10 -%}
<li>
<strong>{{ date | date: 'H:mm:ss' }}</strong> -
{%- if pageview.code == 200 -%}
<a href="{{pageview.url}}">
{{-pageview.title-}}
</a>
{%- else -%}
{{-pageview.code-}}
{%- endif -%}
</li>
{%- endfor -%}
</ol>
Pagination
Page through a collection with query parameters and a stable canonical URL.
When listing content across pages, use request query parameters (for example page number or offset) to fetch the right segment of data. Keep limit and sort stable so page 2 is the next window of the same list, not a reshuffle.
Set the canonical URL for the current page so search engines and users get a stable link. On page 1, prefer the entity’s canonical or full URL; on later pages, append the page query parameter.
The complete pagination partial under Complete Examples shows how to render previous/next (or numbered) links from collection paging properties. Creating and Using Collections covers the fetch side of the same pattern.
ExampleGet request parameters and fetch blog_posts with pagination and tag filterRead page and tag from query params, then fetch blog_posts with limit and sort.
Liquid
{%- assign var postsPerPage = 10 -%}
{%- assign pageParam = 1 -%}
<!-- Did a page param get passed in the url? -->
{%- if request.query_params['page'] -%}
{%- assign pageParam = request.query_params['page'] | to_int -%}
{%- endif -%}
<!-- Did a tag param get passed in the url? -->
{%- if request.query_params['tag'] and request.query_params['tag'] != "" -%}
{%- assign tagFilter = request.query_params['tag'] | url_decode | downcase -%}
{%- endif -%}
{%- blog_posts assign posts = blog:"The Kitchen Essentials" tag:tagFilter limit:postsPerPage page:pageParam sort_by:"post_date" sort_direction:"desc" -%}
{%- for post in posts -%}
{{-post.title-}}
{%- endfor -%}
ExampleSet the canonical URL for paginated pagesSet the canonical URL for paginated pages so search engines understand the preferred page.