# Variables and Data Types

In Liquid, you work with variables to store and manipulate data. This section covers how to create variables and the different data types available.

# Variables

Variables store values that you can reuse in your template. You can create your own variables or use the ones Modyo provides automatically.

# Creating variables

Use assign to create new variables:

{% assign name = "John" %}
{% assign price = 100 %}
{% assign active = true %}

{{ name }} → John
{{ price }} → 100

# Access syntax

{{ variable }}                    → Displays the value
{{ object.property }}           → Accesses a property (dot notation)
{{ object['property'] }}        → Bracket access (necessary if there are spaces)
{{ object.nested.property }}    → Nested properties

Differences between dot notation and brackets:

  • Dot: Simpler but doesn't allow spaces or dynamic variables
  • Brackets: Allows spaces, hyphens, and using variables as keys
{{ product.title }}              → Works
{{ product['title'] }}           → Also works
{{ product['special-price'] }}   → Necessary with hyphens
{{ product[variable_name] }}     → Dynamic access with variable

# Data Types

Liquid in Modyo supports the following data types:

# Strings

Strings must be in single or double quotes:

{% assign message = "Hello world" %}
{% assign name = 'John' %}
{{ message }} → Hello world

# Numbers

{% assign price = 100 %}         → Integer
{% assign discount = 0.15 %}     → Decimal
{{ price | minus: 10 }}          → 90

# Booleans

{% assign active = true %}
{% assign published = false %}

{% if active %}
  This content is active
{% endif %}

# Nil

Represents the absence of value:

{% if user == nil %}
  No user logged in
{% endif %}

# Arrays

Ordered collections of elements:

{% assign products = spaces['store'].types['product'].entries %}

{{ products[0].fields.name }}     → First element (index 0)
{{ products[2] }}                  → Third element (index 2)
{{ products.first.fields.name }}  → First element (.first method)
{{ products.last.fields.name }}   → Last element (.last method)
{{ products.size }}                → Number of elements

{% for product in products %}
  {{ forloop.index }}: {{ product.fields.name }}
{% endfor %}

Note about indices: Arrays in Liquid start at 0, so the first element is [0], the second is [1], etc.

# Hashes (objects/dictionaries)

Collections of key-value pairs:

{% assign config = site.metadata %}

{{ config.theme }}                  → Dot notation
{{ config['theme'] }}               → Bracket notation
{{ config['color-primary'] }}       → Necessary with hyphens

{% for item in config %}
  {{ item[0] }}: {{ item[1] }}     → Key: Value
{% endfor %}

# Operations with Data Types

# Type conversion

{{ "100" | plus: 0 }}              → Converts string to number
{{ 100 | append: "" }}              → Converts number to string

# Type checking

{% if products.size > 0 %}
  Products available
{% elsif products == empty %}
  No products
{% endif %}

# Default values

{{ user.name | default: "Guest" }}
{{ product.fields.price | default: 0 }}

Best Practice

Always use default when you're not sure if a value exists to avoid displaying empty content.

# Special Modyo Objects

Modyo provides predefined objects that contain system information:

  • user: Current user
  • site: Current site
  • page: Current page
  • spaces: Access to content spaces
  • account: Account information

For a complete and detailed list of all available objects, see the Objects section.

# Context Variables

Beyond those objects, on every render the platform injects a set of variables that describe the page being resolved. They aren't content: they are the context the template works with, and they determine what you can write in each view.

Not all of them exist everywhere. Outside the context where it's injected, a variable simply doesn't exist and Liquid prints it empty, with no warning at all, so a misspelled name and a variable used outside its context look exactly the same on the page. See Render Error Behavior.

# Always available

These four collections are injected on every render, no matter the page type and no matter whether you are in a layout, a view, a snippet or a widget template:

Variable What it holds
assets The account's asset manager. Indexed by the file UUID, as in assets['uuid'].
spaces The account's content spaces. Indexed by the space identifier, as in spaces['blog'].
menus The site's menus. Indexed by the menu slug, as in menus['main']. See Navigation.
vars The site's global variables or, in a widget template, that widget's variables. See Global Variables.

# From the page context

Variable What it holds Where it exists
content_for_layout The already rendered HTML of the view. Marks the spot in the layout where the page is inserted. Layouts only
page The current page. See page. All but the search results page
page_context The page type that was resolved, for example context-home, context-custom or context-content. See the full list in The body block. All
page_name The context identifier with a suffix that indicates the view, almost always -show, for example context-content-show. All
page_title The page name. On the home page and on search it is the platform's translated text. All
page_id page_name followed by the page path, for example context-custom-show-contact. On the home page it is just context-home-show. All but the search results page
url The URL being resolved, including the category path or the entry slug when applicable. All but the search results page
current_layout_page The page the layout in use comes from. All but the home page and the search results page
page_grid The page's grid, the one passed to {% snippet page_grid %}. See grid. Home page and custom pages

Tip

The {% body %} tag writes page_context, page_name and page_id into the class attribute of the body element. Looking at a published page's source tells you which context it resolved in, without printing anything in the template.

# From content pages

Variable What it holds
page_scope index when the listing was resolved and show when a single entry was resolved.
entries The listing's entries. Only when page_scope is index.
entry The entry matching the slug in the URL. Only when page_scope is show.
category_path The category path taken from the URL, without the entry slug. Empty when the URL carries no category.
category The category matching category_path.

# From the search results page

Variable What it holds
site_search The resolved search, with its results. See sitesearch.
params_query The search term, sanitized and escaped, ready to print. Only exists if the URL carries the parameter.
params_more The search's extra filter, sanitized and escaped. Only exists if the URL carries it.

# From Origination pages

Origination pages also inject origination, the page's flow; pending_submissions, the user's pending responses in that flow; submission, the response being resolved; and, while that response is in progress, current_step and current_task.