Skip to main content

Condition Expressions

This page covers everything you can write inside a Condition block — operators, nesting, and dynamic data.

A condition is any expression that evaluates to true or false. You type it in the Condition block's sidebar panel, and Unblock wraps your inner blocks with {% if expression %} / {% endif %}.

Existence

The simplest condition — check if a value exists and is not empty:

{% if post.thumbnail %}
{% if post.excerpt %}
{% if post.content %}
{% if user.id %} {# user.id is 0 for guests #}

An existence check returns false for null, false, 0, empty strings, and empty arrays.

Comparison operators

OperatorDescriptionExample
==Equalpost.post_status == 'publish'
!=Not equalpost.post_status != 'draft'
===Identical (strict)post.comment_count === 0
!==Not identical (strict)post.type !== 'page'
>Greater thanpost.tags|length > 0
<Less thanpost.categories|length < 3
>=Greater or equalpost.date('Y') >= '2024'
<=Less or equalpost.date('Y') <= '2025'
{% if post.category.slug == 'featured' %}
{% if post.comment_count > 0 %}
{% if post.date('Y') == '2026' %}
Strict vs loose

Use === and !== (strict) when you need to distinguish between 0, false, null, and empty strings. Use == and != for general comparisons.

Logical operators

OperatorDescriptionExample
andBoth must be truepost.thumbnail and post.excerpt
orAt least one must be truepost.type == 'post' or post.type == 'page'
notNegationnot post.thumbnail
{% if post.thumbnail and post.excerpt %}
{% if post.type == 'post' or post.type == 'page' %}
{% if not post.thumbnail %}

Containment operators

OperatorDescriptionExample
inValue is in a listpost.post_status in ['publish', 'future']
not inValue is not in a listpost.post_status not in ['draft', 'trash']
starts withString starts withpost.title starts with 'How'
ends withString ends withpost.slug ends with '-draft'
{% if post.post_status in ['publish', 'future'] %}
{% if 'sale' in post.tags %}
{% if post.category.slug not in ['uncategorized', 'draft'] %}

Nested conditions

Use parentheses to group logic and control evaluation order:

{% if post.post_status == 'publish' and (post.thumbnail or post.excerpt) %}

Without parentheses, and binds tighter than or. This means:

{# These are NOT the same: #}
{% if a and b or c %} {# → (a and b) or c #}
{% if a and (b or c) %} {# → a and (b or c) #}

You can nest as deep as needed:

{% if user.id and ('editor' in user.roles or ('author' in user.roles and post.author.id == user.id)) %}

Dynamic data in conditions

Conditions have full access to the same dynamic data you use in expressions — providers, methods, filters, and functions all work inside {% if %}.

Provider fields

Any field from any provider works in a condition:

{% if post.author.id == user.id %}
{% if site.language == 'fr' %}
{% if archive.type == 'category' %}
{% if 'administrator' in user.roles %}

Methods

Call methods with arguments:

{% if post.meta('is_featured') %}
{% if post.date('Y') == '2026' %}

Filters

Apply filters to transform values before comparing:

{% if post.title|length > 50 %}
{% if post.content|striptags|length > 1000 %}
{% if post.tags|length >= 3 %}

Variables

Use a Variable block to store a value, then check it in a Condition:

{% set subtitle = post.meta('subtitle') %}

{% if subtitle %}
<p class="subtitle">{{ subtitle }}</p>
{% endif %}

Math

Arithmetic works inside conditions:

{% if loop.index + 1 <= 5 %}
{% if loop.index % 2 == 0 %}
Common mistake

Filters like |length return a number, not a boolean. Writing {% if post.tags|length %} works (non-zero is truthy), but {% if post.tags|length > 3 %} is clearer about your intent.

Next steps