Skip to documentation content

Safe Output and Handling Missing Data

Safe Output and Handling Missing Data

Encode for the destination (HTML text, attributes, URLs, JSON) and guard optional data with is_valid or default.

Templates often depend on data that might be missing: an optional custom field, a query parameter that was not supplied, or a value passed into a partial under another name. The other half of safe output is encoding for the destination. A field printed in an HTML text node is not encoded the same way as that same value in a tag attribute, a CSS class, a URL, or JSON.

Most Marketpath fields (text, textarea, select, and similar) HTML-encode their default output. Printing {{ entity.title }} or {{ item.title }} in an HTML text node is usually correct. Do not pipe that default output through the escape filter — that double-encodes, and it can escape preview markup such as span tags. On the current page, output {{ entity.title }} for the heading; do not treat it as an optional plain string with the default filter, and do not wrap root entity in is_valid. Guard renamed copies such as item when a partial receives entity as an input.

The raw unencoded value is {{ field.value }}. Use .value when you need the string itself: HTML attributes, filter chains, or a partial that expects a plain string. In attributes, always use .value and escape, even on required fields, because default field output is HTML-oriented and may include preview markup that does not belong in an attribute:

<div aria-label="{{ item.title.value | escape }}"></div>

Choose a filter for the destination, not one generic “safe” filter. Use escape (or html_encode, which performs the same encoding) for HTML text and attributes. Use classname for class names, url_encode for query values, and json_encode for JSON. HTML escape does not make a URL or a JSON payload correct. Prefer escape over escape_once for ordinary unencoded text.

Two field types break the usual encoding rule. An html field is trusted markup; escaping it usually defeats the field’s purpose. A code field’s default output is the raw value — treat it as unsafe and use it with extreme caution.

For missing data, prefer {% if item.is_valid %} over a bare {% if item %} when item is a documented object. Bare truthiness is mainly for checking whether an unbound input variable exists. Wrap optional sections (an image, a link, a whole block) in {% if %} so you do not render empty tags or broken markup.

Use the default filter when a fallback is intentional. It returns the fallback when the input is null, an empty string, or invalid (is_valid is false). It does not treat the boolean false as missing. For an optional custom field in HTML:

{{ item['custom-field'].value | default: "Fallback" | escape }}

If you would rather keep the template readable, bind the field with {% var %}, test is_valid, and output the field (for a text node) or .value | escape (for an attribute). When a reusable partial expects a plain string, pass a string at the include boundary (for example title: item.title.value), not the field object.

When an input may be either a plain string or a field object, one chain resolves both: {{ input.value | default: input | default: "Fallback" | escape }}. An empty or missing field falls through to the next default because is_valid is false, and the result is a string suitable for an HTML text node or, unchanged, an attribute.

These habits keep encoding correct and keep missing data from producing errors or empty markup. See the linked examples, the default and escape filters, and the is_valid condition for more detail.
Example Check if an object or value is valid with is_validCheck whether an object or value is valid (non-empty, present) with is_valid before using it.
Liquid
{%- if random_object is_valid -%}
	{%- include "display_random_object" object:random_object -%}
{%- endif -%}

If field.is_valid

Liquid
{%- if field.is_valid -%}
	{{-field.value-}}
{%- endif -%}

Every field and most objects have an is_valid property that can be used to check if the field or object has a valid value (non-null, non-empty, non-false, published entity, etc.). If you expect the field to be an object with an is_valid property, then this is the best way to check if it has a valid value.

If variable is_valid

Liquid
{%- if varname is_valid -%}
	{{-varname-}}
{%- endif -%}

The is_valid conditional expression checks to see if a variable currently holds a valid value (non-null, non-empty, object with is_valid == true, etc.). This works for most cases, whether or not the variable is an object or a simple value

Unless variable is_valid

Liquid
{%- unless varname is_valid -%}
	... do something if varname is NOT valid...
{%- endunless -%}

The unless conditional makes it easy to do something if the is_valid condition does NOT evaluate to true

is_valid conditional when used on false values

Liquid
{%- var falsevar = false -%}
{%- if falsevar is_valid -%}
	false is_valid evaluates to TRUE!
{%- else -%}
	this will never be output
{%- endif -%}

When using the is_valid condition on a false value, it will evaluate to true. This is by design, allowing the is_valid condition to be used to check whether or not a boolean variable has been defined

Example How to use the default filterUse the default filter to output a fallback when a value is null, empty, false, or invalid. Shown for strings, numbers (e.g. query parameters), and optional display.

String default

Liquid
{%- var greeting = "" -%}
{{- greeting | default: "hello" }} world
{%- set greeting = "goodbye" -%}
{{- greeting | default: "hello" }} world
Output
hello world
goodbye world

Output a default value when the input string is null or empty.

Numeric default (e.g. query parameter)

Liquid
{% var page = request.query_params['page'] | to_int | default: 1 %}

Provide a default value for a numeric query parameter when missing or invalid. The to_int filter is used to convert the query parameter to a number if it is not already a number.

Default for an optional custom field

Liquid
{{ item['headline'].value | default: "Untitled" | escape }}

Take .value so default sees a string (or an invalid/empty field), then escape for HTML. Do not pipe a field’s default output through default as if it were a plain string, and do not use default on entity.title.

Default with filters in a chain

Liquid
{{ classname_prefix | strip | default: 'fallback-' | append: entity.name.value | classname }}

Combine default with other filters; filters are applied in the order they are listed.

Example Output a field in an HTML attributeUse .value and escape when printing a field inside an HTML tag attribute. Default field output is for HTML text nodes, not attributes.
Liquid
<div aria-label="{{ item.title.value | escape }}"></div>
Example Escape HTML (escape and escape_once)Escape HTML characters for safe output inside other HTML. Use escape for full escaping; use escape_once when content may already be partially escaped to avoid double-encoding.

escape

Liquid
<p title="{{"<p>A string with HTML & other characters</p>" | escape}}">...
Output
<p title="&lt;p&gt;A string with HTML &amp; other characters&lt;/p&gt;">...

Escapes all HTML and special characters.

escape_once

Liquid
<p title="{{"&lt;p&gt;A string with some characters encoded & others not encoded&lt;/p>" | escape_once}}">...
Output
<p title="&lt;p&gt;A string with some characters encoded &amp; others not encoded&lt;/p&gt;">...

Escapes only characters that are not already escaped, avoiding double-encoding.