Skip to documentation content

Variables Overview

Variables Overview

How variables (objects) are defined, scoped, and used in templates.

Variables are objects that are available for reference by the template. Some variables are reserved for use by Marketpath CMS and are defined by the request or page, while others may be created or modified using template markup. All variables are stored on a scope and are accessible to all markup inside that scope.

A variable can also supply a method’s argument name or its whole argument list, using the reference (&) and variable (*) argument forms described on Methods Overview.

Variable Scopes

How variable scope works, where scopes are created, and how var, set, and assign control where variables are stored.

Scope is the context in which a variable is accessible. Scopes may be nested similarly to how partial templates may be nested - with a root scope which may contain child scopes, each of which may also contain child scopes.

Any object on a parent scope is accessible in all child scopes unless the child scope has another object by the same name. Objects on a child scope, however, are no longer accessible on the parent scope once the child scope is complete.

Additionally, if an object exists by the same name on both a parent and a child scope, only the object on the child scope will be accessible until the child scope is complete.

When variable scoping is utilized correctly, it is possible for multiple templates to use the same variable names without affecting each other's behavior. Alternatively, if the template developer needs a template to modify the behavior of another template, they may use the root scope to share information between templates that are not nested.

The root scope is the top-most scope used by the initial page template.

Child scopes are created inside most block methods (eg: {% if %}, {% for %}, {% capture %}, etc...) as well as inside partial templates. The child scope is deleted when the block method is closed (eg: {% endif %}, {% endfor %}, {% endcapture %}, etc...) or the partial template finishes rendering.

The liquid developer documentation should indicate which block methods create child scopes.

While it is possible to create templates without a good understanding of scope, it may be helpful to understand how liquid variables are scoped in order to create truly reusable templates or integrate smoothly with code written by other developers.

By using variable scopes effectively, developers can create and edit intuitive and reusable templates without interfering with functionality implemented by other template developers on the same site. All the developer needs to do is store their objects on the child scope and when their template is done rendering it will not have disturbed any of the parent template objects or variables. For example: you could use the variable "start" even if the same variable is used by a parent template, and when your template is done rendering the "start" object would be unchanged in the parent scope.

There are three different ways to save objects on the scope which are designed to give you full control over where objects are saved without becoming overly complicated:
• var stores the variable on the current scope. Once the current scope is completed, the variable will no longer be accessible. This is the default functionality for everything except for the {% assign %} and {% set %} methods.
• set updates the variable on the closest scope which it is already defined on. If the variable has not been defined on any scope, it will be stored on the root scope.
• assign removes the variable from all scopes and stores it on the root scope.

When writing templates, the best practice is to utilize var and set wherever possible and reserve assign for cases where it is specifically necessary. By restricting your variables to the scopes where they are used, you make your code more friendly and compatible with code written by other developers who may wish to use the same variable names.

Note that there is currently no way to unset a variable from a scope other than using the assign method to define the variable at the root scope. Setting a variable to null does not remove it from the scope.
Example How to use the var, set, and assign methodsUse var (current scope), set (nearest existing scope), and assign (root scope) to store values; filters can modify the value.

Var sets the value on the current scope

Liquid
{{ test1 }}
{%- var test1 = 'out1' -%}
,{{ test1 }}
{%- if true -%}
	,{{ test1 }}
	{%- var test1 = 'out2' -%}
	,{{ test1 }}
{%- endif -%}
,{{ test1 }}
Output
,out1,out1,out2,out1

The first time the variable is output it has not been set yet so it does not output anything. The second time it has been set to out1. The third time it has not been set on the current scope but it is still set on the parent scope so it outputs out1 again. The next time it has been set to out2 on the current scope so that is output instead. When the current scope ends (with the endif method) the variable is still set on the parent scope so it outputs out1 again.

Set changes the value on the closest scope

Liquid
{%- var test2 = 'out1' -%}
{{ test2 }}
{%- if true -%}
	{%- var test2 = 'out2' -%}
	,{{ test2 }}
	{%- set test2 = 'out3' -%}
	,{{ test2 }}
	{%- if true -%}
		{%- set test2 = 'out4' -%}
		,{{ test2 }}
	{%- endif -%}
	,{{ test2 }}
{%- endif -%}
,{{ test2 }}
Output
out1,out2,out3,out4,out4,out1

The first time the variable is output it has been set to out1 on the root scope. The second time it has been set to out2 on the current scope using the var method so it is still unchanged on the root scope. The third time it has been updated to out3 on the nearest scope that it is defined for, which also happens to be the current scope. The fourth time it has been updated to out4 on the parent scope, which is the nearest scope that it is defined for. When the current scope ends and returns to the parent scope, the updated value is still present. When that scope ends and it returns to the root scope, the variable returns to the value that was set on the root scope.

Assign sets the value on the root scope and removes the variable from all other scope

Liquid
{{ test3 }}
{%- if true -%}
	{%- var test3 = 'out1' -%}
	,{{ test3 }}
	{%- if true -%}
		{%- assign test3 = 'out2' -%}
		,{{ test3 }}
	{%- endif -%}
	,{{ test3 }}
{%- endif -%}
,{{ test3 }}
Output
,out1,out2,out2,out2

The first time the variable is output it has not been set yet so it does not output anything. The second time it has been set to out1 on the current scope (not the root scope). The third time it has been set to out2 on the root scope by the assign method, which also clears it from all other scopes. Then the fourth and fifth times it outputs out2 from the root scope.

Store a value in a variable using a filter

Liquid
{%- var title = entity.name | default: 'Untitled' -%}
<p>{{ title }}</p>

Stores the result of the expression in a variable for reuse.

Create an empty variable, then update it as needed using the set method

Liquid
{%- var tag = '' -%}
{%- if entity.tags is_list and entity.tags.is_valid -%}
	{%- set tag=entity.tags | first -%}
{%- endif -%}
{%- datastore_items datastore:"products" tag:tag -%}

Conditionally stores the first tag in the tags list in the tag variable. Then uses the tag variable to filter the datastore items. Using var and set in this way prevents the tag variable from being overwritten in the parent scope.

Duplicate a variable, then update it as needed to preserve the original value

Liquid
{%- var tag = tag -%}
{%- if entity.tags is_list and entity.tags.is_valid -%}
	{%- set tag=entity.tags | first -%}
{%- endif -%}
{%- datastore_items datastore:"products" tag:tag -%}

By creating a new variable on the current scope from an existing variable on the parent scope, we can update the variable using the set method without affecting the parent scope. Note that even if the variable is not set on the parent scope the current scope will still define the variable with a null value.

Use assign to force the variable to be accessible from the root scope

Liquid
{%- if entity.tags is_list and entity.tags.is_valid -%}
	{%- assign num_tags = entity.tags | size -%}
{%- endif -%}
There are {{num_tags | default:0}} tags on this entity

The assign method creates a root-scope variable, so it is accessible from the root scope and all child scopes.

Example Get Calendar Entries for the Next MonthSet a date range with midnight and add_months, then fetch the next 30 calendar_entries sorted by start_date.
Liquid
{%- var minDate = "now" | midnight -%}
{%- var maxDate = "now" | midnight | add_months: 1 -%}
{%- calendar_entries var entries = start_date:minDate end_date:maxDate limit:30 sort_by:"start_date" sort_direction:"asc" -%}
Example Output 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