Skip to main content

Liquid Filters

Filters modify the output of variables. They are applied using the pipe character |.

{{ "hello world" | capitalize | append: "!" }}
<!-- Output: Hello world! -->
Standard Liquid

OQO extends Liquid, the templating language created by Shopify. All standard Liquid filters work in OQO themes, plus the custom filters documented below.


URL Filters​

Generate URLs for models and assets.

tag_url​

Generate URL for a tag.

<a href="{{ tag | tag_url }}">{{ tag.name }}</a>
<!-- Output: /tag/technology -->

<!-- In a loop -->
{% for tag in post.tags %}
<a href="{{ tag | tag_url }}">{{ tag.name }}</a>
{% endfor %}

collection_url​

Generate URL for a collection.

<a href="{{ collection | collection_url }}">{{ collection.title }}</a>
<!-- Output: /collection/featured-stories -->

user_url​

Generate URL for a user profile.

<a href="{{ post.author | user_url }}">{{ post.author.name }}</a>
<!-- Output: /author/john-doe -->

absolute_url​

Convert relative path to absolute URL.

{{ post.url | absolute_url }}
<!-- Output: https://yoursite.com/post/my-article -->

asset_url​

Generate URL for a non-image asset (PDF, file, etc.).

{{ 'styles.css' | asset_url }}
{{ section.settings.pdf_file | asset_url }}

download_url​

Generate download URL that forces browser download.

<a href="{{ section.settings.file | download_url }}">Download PDF</a>

Filters for navigating between posts within a collection. Useful for building prev/next post navigation.

next_post​

Get the next post in a collection (ordered by publication date).

{% assign next = collection | next_post: post %}
{% if next %}
<a href="{{ next.url }}">Next: {{ next.title }}</a>
{% endif %}

prev_post​

Get the previous post in a collection.

{% assign prev = collection | prev_post: post %}
{% if prev %}
<a href="{{ prev.url }}">Previous: {{ prev.title }}</a>
{% endif %}

first_post​

Get the first post in a collection.

{% assign first = collection | first_post %}
{% if first %}
<a href="{{ first.url }}">Start reading</a>
{% endif %}

last_post​

Get the last post in a collection.

{% assign last = collection | last_post %}
{% if last %}
<a href="{{ last.url }}">Latest post</a>
{% endif %}

Complete Prev/Next Navigation Example​

Build a full post navigation bar for a post detail page:

{% if collection %}
<nav class="post-navigation">
{% assign prev = collection | prev_post: post %}
{% assign next = collection | next_post: post %}

{% if prev %}
<a href="{{ prev.url }}" class="nav-prev">
&larr; {{ prev.title }}
</a>
{% endif %}

{% if next %}
<a href="{{ next.url }}" class="nav-next">
{{ next.title }} &rarr;
</a>
{% endif %}
</nav>
{% endif %}

Image Filters​

All image filters work with ImageDrop objects from posts, collections, users, elements, or settings. The image_url filter is the core of the image system -- it handles resizing, cropping, format conversion, and advanced effects like grayscale, duotone, watermarks, and film grain, all server-side via libvips.

image_url​

Get a URL for an image with a specific size. Accepts preset names, custom geometry strings, or hash options.

Presets​

{{ post.image | image_url: 'thumb' }}      <!-- 100x100 -->
{{ post.image | image_url: 'small' }} <!-- 320x240 -->
{{ post.image | image_url: 'medium' }} <!-- 640x480 -->
{{ post.image | image_url: 'large' }} <!-- 1024x768 -->
{{ post.image | image_url: 'hero' }} <!-- 1920x1080 -->
{{ post.image | image_url: 'avatar' }} <!-- 100x100 cropped -->
{{ post.image | image_url: 'square' }} <!-- 400x400 cropped -->
{{ post.image | image_url: 'og' }} <!-- 1200x630 for social -->
PresetSizeMode
thumb100x100Fit within
thumbnail150x150Fit within
small320x240Fit within
medium640x480Fit within
large1024x768Fit within
hero1920x1080Fit within
og1200x630Fit within
square400x400Crop to fill
avatar100x100Crop to fill

Custom Geometry (Legacy)​

{{ post.image | image_url: '400x300' }}    <!-- Fit within bounds -->
{{ post.image | image_url: '400x300#' }} <!-- Crop to exact size -->
{{ post.image | image_url: '400x300>' }} <!-- Only shrink if larger -->
{{ post.image | image_url: '400x' }} <!-- Width only, auto height -->

Hash Options​

For full control, pass a hash with named options:

<!-- Resize -->
{{ post.image | image_url: width: 400 }}
{{ post.image | image_url: width: 400, height: 300 }}

<!-- Crop with gravity -->
{{ post.image | image_url: width: 400, height: 300, crop: true }}
{{ post.image | image_url: width: 400, height: 300, crop: 'north' }}

<!-- Format and quality -->
{{ post.image | image_url: width: 800, format: 'webp' }}
{{ post.image | image_url: width: 800, quality: 85 }}

Crop gravity options: center (default), north, south, east, west, north_west, north_east, south_west, south_east

Baseline Grid Alignment​

Snap image heights to a typographic baseline grid. Calculates the proportional height, rounds up to the nearest baseline multiple, and crops to that exact height.

{{ post.image | image_url: width: 600, baseline: 24 }}

A 600x400 image with a 24px baseline would get height = ceil(400/24) * 24 = 408px, then crop to 600x408.

Image Effects​

Server-side image effects powered by libvips. Combine with resize options:

<!-- Grayscale -->
{{ post.image | image_url: width: 800, grayscale: true }}

<!-- Sepia (warm brown tint) -->
{{ post.image | image_url: width: 800, sepia: true }}

<!-- Duotone (maps grayscale to two colors) -->
{{ post.image | image_url: width: 800, duotone_dark: '#1a1a2e', duotone_light: '#edf2f4' }}

<!-- Tint / monochrome (colorize with a single color) -->
{{ post.image | image_url: width: 800, tint: '#3b82f6' }}

<!-- Blur -->
{{ post.image | image_url: width: 800, blur: 5.0 }}

<!-- Sharpen / harden (sharpen + contrast boost) -->
{{ post.image | image_url: width: 800, harden: 50 }}

<!-- Soften (gentle blur) -->
{{ post.image | image_url: width: 800, soften: 30 }}

<!-- Film grain -->
{{ post.image | image_url: width: 800, grain: 50 }}

<!-- Remove background color (make a color transparent) -->
{{ post.image | image_url: color_to_alpha: '#ffffff' }}
{{ post.image | image_url: color_to_alpha: '#000000' }}

Watermark​

Add a text watermark overlay to images:

<!-- Simple text watermark -->
{{ post.image | image_url: width: 800, watermark: '© My Brand' }}

For more control, use hash options:

{{ post.image | image_url: width: 800,
watermark: { text: '© My Brand', position: 'bottom-right', opacity: 30 } }}

Watermark options:

OptionTypeDefaultDescription
textstring-Watermark text (required)
positionstring-Placement: top-left, top-right, bottom-left, bottom-right
opacityinteger-Opacity (0-100)
sizeinteger-Font size
colorstring-Hex color (e.g., #ffffff)
shadowboolean-Add drop shadow
margininteger-Distance from edge in pixels

Works with Any Image Source​

{{ post.image | image_url: 'medium' }}
{{ collection.image | image_url: 'hero' }}
{{ post.author.image | image_url: 'avatar' }}
{{ section.settings.background_image | image_url: 'hero' }}

The filter handles ImageDrop objects, ActiveStorage attachments, Asset IDs (from section settings), and raw URLs.

image_srcset​

Generate a srcset attribute for responsive images. Automatically creates URLs at small (320w), medium (640w), large (1024w), and hero (1920w) sizes.

<img src="{{ post.image | image_url: 'medium' }}"
srcset="{{ post.image | image_srcset }}"
sizes="(max-width: 640px) 100vw, 50vw"
alt="{{ post.image.alt | default: post.title }}">

image_tag​

Generate a complete <img> tag with loading="lazy" automatically included.

{{ post.image | image_tag: 'medium', 'Alt text', 'my-class' }}
<!-- Output: <img src="..." alt="Alt text" class="my-class" loading="lazy"> -->

Parameters: image_tag: preset_or_options, alt_text, css_class


Color Filters​

Shopify-style color manipulation for dynamic theming.

color_lighten​

Lighten a color by percentage.

{{ '#333333' | color_lighten: 20 }}
<!-- Output: #666666 (lighter) -->

color_darken​

Darken a color by percentage.

{{ '#ffffff' | color_darken: 15 }}
<!-- Output: #d9d9d9 (darker) -->

color_saturate / color_desaturate​

Adjust color saturation.

{{ theme.settings.primary_color | color_saturate: 30 }}
{{ theme.settings.primary_color | color_desaturate: 30 }}

color_mix​

Mix two colors together.

{{ '#ff0000' | color_mix: '#0000ff', 50 }}
<!-- Output: purple (50% red, 50% blue) -->

color_brightness​

Get perceived brightness value (0-255).

{{ section.settings.bg_color | color_brightness }}
<!-- Output: 128 (mid-range) -->

color_brightness_class​

Returns 'light' or 'dark' based on brightness.

<div class="bg-{{ section.settings.bg_color | color_brightness_class }}">
<!-- Adds class="bg-light" or class="bg-dark" -->
</div>

color_contrast​

Get contrasting text color (black or white).

<span style="color: {{ section.settings.bg_color | color_contrast }}">
Readable text on any background
</span>

color_to_rgba​

Convert hex color to rgba() CSS value.

{{ '#ff0000' | color_to_rgba: 0.5 }}
<!-- Output: rgba(255, 0, 0, 0.5) -->

color_to_hsl​

Convert hex color to hsl() CSS value.

{{ '#ff0000' | color_to_hsl }}
<!-- Output: hsl(0, 100%, 50%) -->

color_alpha_to_rgba​

Convert color_alpha setting (color + opacity) to rgba().

{{ section.settings.overlay_color | color_alpha_to_rgba }}
<!-- Input: {"color": "#000000", "alpha": 50} -->
<!-- Output: rgba(0, 0, 0, 0.5) -->

Usage in CSS variables:

{% capture section_style %}
--overlay-color: {{ section.settings.overlay | color_alpha_to_rgba }};
--bg-color: {{ section.settings.bg_color | color_to_rgba: 0.9 }};
{% endcapture %}

{% section_wrapper 'section' style: section_style %}
<div style="background: var(--overlay-color);">...</div>
{% endsection_wrapper %}

Text Filters​

Text manipulation and formatting.

pluralize​

Pluralize a word based on count (includes count in output).

{{ 1 | pluralize: 'item' }}
<!-- Output: 1 item -->

{{ 5 | pluralize: 'item' }}
<!-- Output: 5 items -->

{{ 3 | pluralize: 'category', 'categories' }}
<!-- Output: 3 categories -->

pluralize_word​

Pluralize without including the count.

{{ collection.posts_count }} {{ collection.posts_count | pluralize_word: 'post' }}
<!-- Output: 12 posts -->

excerpt​

Extract plain text excerpt from HTML content.

{{ post.content | excerpt: 200 }}
{{ post.content | excerpt: 100, '...' }}

titlecase​

Convert to title case.

{{ 'hello world' | titlecase }}
<!-- Output: Hello World -->

sentencecase​

Convert to sentence case.

{{ 'hello world' | sentencecase }}
<!-- Output: Hello world -->

highlight​

Highlight search terms in text.

{{ post.title | highlight: 'search term' }}
<!-- Output: My <mark>Search Term</mark> Article -->

{{ post.title | highlight: 'term', 'strong' }}
<!-- Output: My <strong>term</strong> Article -->

parameterize​

Convert text to URL-safe slug.

{{ 'Hello World!' | parameterize }}
<!-- Output: hello-world -->

Date Filters​

Date formatting and relative time display.

date_format​

Format a date with presets or custom format.

{{ post.published_at | date_format: 'short' }}
<!-- Output: Jan 15 -->

{{ post.published_at | date_format: 'medium' }}
<!-- Output: January 15, 2025 -->

{{ post.published_at | date_format: 'long' }}
<!-- Output: Wednesday, January 15, 2025 -->

{{ post.published_at | date_format: 'iso' }}
<!-- Output: 2025-01-15 -->

{{ post.published_at | date_format: '%B %Y' }}
<!-- Custom strftime format -->

Date Format Presets:

PresetOutput
shortJan 15
mediumJanuary 15, 2025
longWednesday, January 15, 2025
fullWednesday, January 15, 2025 at 03:30 PM
iso2025-01-15
time03:30 PM
time_2415:30
datetime2025-01-15 15:30
rssWed, 15 Jan 2025 15:30:00 +0000
year2025
monthJanuary 2025
day15

time_ago​

Get relative time description.

{{ post.published_at | time_ago }}
<!-- Output: 2 days ago, about 1 hour ago, just now, etc. -->

relative_date​

Get relative date with smart formatting.

{{ post.published_at | relative_date }}
<!-- Output: Today, Yesterday, 3 days ago, or January 15, 2025 -->

past? / future?​

Check if date is in past or future.

{% if event.date | past? %}
<span class="badge">Event has passed</span>
{% endif %}

{% if event.date | future? %}
<span class="badge">Upcoming</span>
{% endif %}

year / month_name​

Extract date parts.

{{ post.published_at | year }}
<!-- Output: 2025 -->

{{ post.published_at | month_name }}
<!-- Output: January -->

Work with link settings from section schemas.

Extract URL from link setting.

<a href="{{ section.settings.button | link_href }}">
Click here
</a>

Extract text from link setting.

<a href="{{ section.settings.button | link_href }}">
{{ section.settings.button | link_text }}
</a>

Get target attribute for link.

<a href="{{ section.settings.link | link_href }}"
target="{{ section.settings.link | link_target }}">
{{ section.settings.link | link_text }}
</a>

opens_new_window​

Check if link opens in new window.

<a href="{{ section.settings.link | link_href }}"
{% if section.settings.link | opens_new_window %}target="_blank" rel="noopener"{% endif %}>
{{ section.settings.link | link_text }}
</a>

Utility Filters​

Common utilities for settings and CSS.

true? / false?​

Boolean checks for non-boolean setting values (strings, objects).

<!-- For boolean settings (checkboxes), use natural boolean checks -->
{% if section.settings.show_title %}
<h1>{{ section.settings.title }}</h1>
{% endif %}

{% unless section.settings.hide_image %}
<img src="{{ post.image | image_url: 'medium' }}">
{% endunless %}

<!-- These filters are for checking string/object values -->
{% if some_string_value | true? %}
<!-- Checks if string equals "true" -->
{% endif %}

Note: Checkbox settings now return raw true/false values, so you can use natural boolean checks (if, unless). The .true? and .false? filters are still available for checking string values.

setting_value​

Extract raw value from setting object.

{{ section.settings.theme | setting_value }}

background_style​

Build CSS background-image style.

<div style="{{ section.settings.image | image_url: 'hero' | background_style }}">
</div>
<!-- Output: background-image: url(...); -->

gradient_overlay​

Build CSS gradient overlay style.

<div style="{{ section.settings.color1 | gradient_overlay: section.settings.color2 }}">
</div>

add_class​

Join CSS classes together, filtering out blanks.

{% assign base_class = 'card' %}
{% assign extra_class = 'featured' %}
<div class="{{ base_class | add_class: extra_class }}">
<!-- Output: class="card featured" -->

if_true​

Return a value when the input is truthy, empty string otherwise.

<div class="article {{ section.settings.featured | if_true: 'is-featured' }}">
<!-- Output: class="article is-featured" (if featured is true) -->

<div class="grid {{ section.settings.flipped | if_true: 'order-2' }}">

append_if​

Append a suffix to a string only when a condition is truthy. More general than if_true -- works with any string, not just CSS classes.

{%- assign section_class = 'relative' | append_if: section.settings.edge_to_edge, ' gz-section-bleed' -%}
<div class="{{ section_class }}">

{{ base_url | append_if: section.settings.show_anchor, '#signup' }}

Filter Chaining​

Filters can be chained together for powerful transformations:

<!-- Responsive image with fallback alt -->
<img src="{{ post.image | image_url: 'medium' }}"
srcset="{{ post.image | image_srcset }}"
alt="{{ post.image.alt | default: post.title | escape }}">

<!-- Excerpt with word limit -->
{{ post.content | strip_html | truncate_words: 30 }}

<!-- Dynamic text color based on background -->
<div style="
background: {{ section.settings.bg_color }};
color: {{ section.settings.bg_color | color_contrast }};
">

<!-- Relative date with fallback -->
<time>{{ post.published_at | relative_date | default: 'Unpublished' }}</time>

<!-- Conditional class with color-based modifier -->
<section class="hero bg-{{ section.settings.bg_color | color_brightness_class }}">

Standard Liquid Filters​

These commonly-used filters are built into Liquid:

String Filters​

{{ "hello" | capitalize }}        <!-- Hello -->
{{ "HELLO" | downcase }} <!-- hello -->
{{ "hello" | upcase }} <!-- HELLO -->
{{ "hello world" | truncate: 8 }} <!-- hello... -->
{{ " hello " | strip }} <!-- hello -->
{{ "hello" | append: " world" }} <!-- hello world -->
{{ "hello" | prepend: "say " }} <!-- say hello -->
{{ "hello" | replace: "l", "L" }} <!-- heLLo -->

Array Filters​

{{ array | first }}               <!-- First item -->
{{ array | last }} <!-- Last item -->
{{ array | size }} <!-- Number of items -->
{{ array | join: ", " }} <!-- Comma-separated -->
{{ array | sort }} <!-- Sorted -->
{{ array | reverse }} <!-- Reversed -->
{{ array | uniq }} <!-- Unique only -->
{{ posts | map: 'title' }} <!-- Extract property -->
{{ posts | where: 'featured' }} <!-- Filter by property -->

Number Filters​

{{ 4.5 | ceil }}                  <!-- 5 -->
{{ 4.5 | floor }} <!-- 4 -->
{{ 4.5 | round }} <!-- 5 -->
{{ 1000 | plus: 500 }} <!-- 1500 -->
{{ 1000 | minus: 500 }} <!-- 500 -->

Date Filter​

{{ post.published_at | date: "%B %d, %Y" }}  <!-- January 15, 2025 -->
{{ post.published_at | date: "%Y-%m-%d" }} <!-- 2025-01-15 -->

Default Filter​

{{ section.settings.title | default: 'Untitled' }}
{{ post.excerpt | default: post.title }}

  • Liquid Objects - Data objects like post, collection, section, member
  • Liquid Tags - Control flow and custom tags including zone, extends, style, post
  • Settings System - Defining section and theme settings