Skip to main content

Liquid Objects

Objects contain data that you can access in your templates using dot notation.

{{ post.title }}
{{ section.settings.heading }}
{{ collection.posts.first.image | image_url: 'medium' }}
Standard Liquid

OQO extends Liquid, the templating language created by Shopify. The objects below are OQO-specific additions.


Global Context Variables

Every Liquid template in OQO (sections and layouts) has access to these top-level variables.

Available in All Templates

VariableTypeDescription
siteSiteDropCurrent site name, URL, and OAuth configuration
settingsHashResolved theme settings (from settings_data.json)
typographyTypographyDropBaseline grid calculations from theme settings
requestRequestDropCurrent request path and locale
postsPostsDropAccess individual posts by slug
tagsarrayAll tags ordered by name
collectionsCollectionsDropAccess collections by slug or iterate all
blogsBlogsDropAccess blogs by slug
memberMemberDropCurrent authenticated member, or nil
member_logged_inbooleantrue when a member is signed in
flashFlashDropFlash messages (notice, alert, error)
routesRoutesDropMember auth route paths

Additional Variables in Section Templates

VariableTypeDescription
sectionSectionDropCurrent section data, settings, and blocks

Additional Variables in Layout Templates

VariableTypeDescription
pagePageDropCurrent page being rendered
content_for_layoutstringRendered section HTML
layout_zonesHashRendered HTML per layout zone
contentDropContent being rendered (post, collection, tag, or page)
content_typestring"post", "page", "collection", or "tag"
parentsarrayParent chain for breadcrumbs
parentDropImmediate parent in URL hierarchy

Unified Content Interface

When rendering via cascading URLs, the content variable provides a consistent API regardless of content type:

<h1>{{ content.title }}</h1>
<p>{{ content.description }}</p>
<a href="{{ content.canonical_url }}">Permalink</a>

{% if content.type == 'post' %}
<span>By {{ content.author.name }}</span>
{% endif %}

All content drops (PostDrop, PageDrop, CollectionDrop, TagDrop) share these properties:

PropertyTypeDescription
typestringContent type identifier
titlestringDisplay title
slugstringURL slug
urlstringFull URL path
canonical_urlstringShortest URL path
descriptionstringMeta description or teaser
imageImageDropCover/featured image or nil

ImageDrop

The unified image wrapper used by ALL images in the system.

Every image in OQO -- whether from posts, collections, users, galleries, or settings -- returns an ImageDrop. This ensures a consistent API across the entire platform.

Properties

PropertyTypeDescription
altstringAlt text for accessibility
captionstringImage caption
widthintegerWidth in pixels
heightintegerHeight in pixels
aspect_ratiofloatWidth divided by height
filenamestringOriginal filename
content_typestringMIME type (e.g., "image/jpeg")
attached?booleanTrue if image exists
present?booleanAlias for attached?
blank?booleanTrue if no image

Using Images

Always use the image_url filter to generate URLs with proper sizing:

{%- comment -%} Presets {%- endcomment -%}
{{ image | image_url: 'thumb' }} {%- comment -%} 100x100 {%- endcomment -%}
{{ image | image_url: 'small' }} {%- comment -%} 320x240 {%- endcomment -%}
{{ image | image_url: 'medium' }} {%- comment -%} 640x480 {%- endcomment -%}
{{ image | image_url: 'large' }} {%- comment -%} 1024x768 {%- endcomment -%}
{{ image | image_url: 'hero' }} {%- comment -%} 1920x1080 {%- endcomment -%}
{{ image | image_url: 'avatar' }} {%- comment -%} 100x100 cropped {%- endcomment -%}
{{ image | image_url: 'square' }} {%- comment -%} 400x400 cropped {%- endcomment -%}
{{ image | image_url: 'og' }} {%- comment -%} 1200x630 for social {%- endcomment -%}

{%- comment -%} Custom geometry {%- endcomment -%}
{{ image | image_url: '400x300' }} {%- comment -%} Fit within bounds {%- endcomment -%}
{{ image | image_url: '400x300#' }} {%- comment -%} Crop to exact size {%- endcomment -%}
{{ image | image_url: '400x' }} {%- comment -%} Width only, auto height {%- endcomment -%}

Examples

{%- comment -%} Post cover image {%- endcomment -%}
{% if post.image %}
<img src="{{ post.image | image_url: 'large' }}"
alt="{{ post.image.alt | default: post.title }}"
width="{{ post.image.width }}"
height="{{ post.image.height }}">
{% endif %}

{%- comment -%} Collection header image {%- endcomment -%}
{% if collection.image %}
<div class="hero" style="background-image: url({{ collection.image | image_url: 'hero' }})">
<h1>{{ collection.title }}</h1>
</div>
{% endif %}

{%- comment -%} User avatar {%- endcomment -%}
<img src="{{ post.author.image | image_url: 'avatar' }}"
alt="{{ post.author.name }}">

{%- comment -%} Section setting image {%- endcomment -%}
<img src="{{ section.settings.background_image | image_url: 'hero' }}">

PostDrop

Represents a blog post. Available inside collection loops and with the {% post %} tag.

Basic Properties

PropertyTypeDescription
idintegerUnique post ID
titlestringPost title
slugstringURL slug
urlstringFull URL path (e.g., /my-post)
canonical_urlstringShortest URL path
teaserstringShort summary text
excerptstringAuto-generated excerpt (200 chars)
statusstringPost status
published_atdatetimePublication date
created_atdatetimeCreation date
updated_atdatetimeLast update date

Display Helpers

PropertyTypeDescription
published_datestringLocalized date (e.g., "January 6, 2026")
published_time_agostringRelative time (e.g., "2 days ago")
reading_timestringEstimated read time (e.g., "5 min read")

Images

PropertyTypeDescription
imageImageDropCover image
cover_imageImageDropAlias for image
imagesarrayFirst gallery's images
galleriesarrayAll gallery elements
{%- comment -%} Cover image with fallback {%- endcomment -%}
{% if post.image %}
<img src="{{ post.image | image_url: 'medium' }}"
alt="{{ post.image.alt | default: post.title }}">
{% else %}
<div class="placeholder">No image</div>
{% endif %}

{%- comment -%} Gallery images {%- endcomment -%}
{% for img in post.images %}
<img src="{{ img | image_url: 'small' }}" alt="{{ img.alt }}">
{% endfor %}

Author

PropertyTypeDescription
authorUserDropPost author
author.full_namestringFull display name
author.display_namestringUsername or full name
author.imageImageDropAvatar image
author.biostringBiography text
<div class="author">
<img src="{{ post.author.image | image_url: 'avatar' }}" alt="">
<span>{{ post.author.display_name }}</span>
</div>

Tags

PropertyTypeDescription
tagsTagsCollectionDropAll tags for this post
{% for tag in post.tags %}
<a href="{{ tag | tag_url }}">{{ tag.name }}</a>
{% endfor %}

{%- comment -%} Primary tag (most distinctive) {%- endcomment -%}
{% if post.tags.primary %}
<span class="tag-label">{{ post.tags.primary.name }}</span>
{% endif %}

Elements (Post Content)

PropertyTypeDescription
elementsElementsCollectionDropAll content elements
has_elementsbooleanTrue if post has elements
textsarrayAll rich text contents as strings
{%- comment -%} Render all elements {%- endcomment -%}
{% for element in post.elements %}
{% case element.type %}
{% when 'rich_text' %}
<div class="prose">{{ element.content }}</div>
{% when 'media' %}
{% if element.image? %}
<img src="{{ element.image | image_url: 'large' }}" alt="{{ element.image.alt }}">
{% endif %}
{% endcase %}
{% endfor %}

ElementsCollectionDrop

A powerful wrapper for post content elements with filtering and grouping capabilities. Access it via post.elements.

Basic Usage

{%- comment -%} Iterate all elements {%- endcomment -%}
{% for element in post.elements %}
{{ element.type }} -- {{ element.slot }}
{% endfor %}

{%- comment -%} Array-like access {%- endcomment -%}
{{ post.elements.size }}
{{ post.elements.first.type }}

Slot Filtering

Filter elements by their layout slot position:

MethodReturnsDescription
elements.leftarrayLeft slot elements (50% width)
elements.rightarrayRight slot elements (50% width)
elements.fullarrayFull-width elements (100% width)
{%- comment -%} Simple two-column layout {%- endcomment -%}
<div class="columns">
<div class="column-left">
{% for element in post.elements.left %}
{% render 'story_element', element: element %}
{% endfor %}
</div>
<div class="column-right">
{% for element in post.elements.right %}
{% render 'story_element', element: element %}
{% endfor %}
</div>
</div>

{%- comment -%} Count elements per slot {%- endcomment -%}
Left: {{ post.elements.left.size }}
Right: {{ post.elements.right.size }}
Full: {{ post.elements.full.size }}

Grouping: by_columns

Groups elements into sections for proper two-column rendering. Full-width elements act as section breaks between column pairs.

Returns: An array of section hashes:

  • { "width" => "half", "left" => [...], "right" => [...] } -- two-column section
  • { "width" => "full", "element" => element } -- full-width element
{% for col_section in post.elements.by_columns %}
{% if col_section.width == 'full' %}
<div class="post-full">
{% render 'story_element', element: col_section.element %}
</div>
{% else %}
<div class="post-columns">
<div class="post-column--left">
{% for element in col_section.left %}
{% render 'story_element', element: element %}
{% endfor %}
</div>
<div class="post-column--right">
{% for element in col_section.right %}
{% render 'story_element', element: element %}
{% endfor %}
</div>
</div>
{% endif %}
{% endfor %}

Grouping: by_type

Groups elements by their type (rich_text, media, etc.).

{%- comment -%} Render all text first, then all media {%- endcomment -%}
{% for element in post.elements.by_type.rich_text %}
<div class="prose">{{ element.content }}</div>
{% endfor %}

{% for element in post.elements.by_type.media %}
{% render 'media_element', element: element %}
{% endfor %}

Grouping: by_media_type

Groups media elements by their media_type (image, gallery, video).

{%- comment -%} All galleries {%- endcomment -%}
{% for element in post.elements.by_media_type.gallery %}
{% render 'gallery', element: element %}
{% endfor %}

{%- comment -%} All videos {%- endcomment -%}
{% for element in post.elements.by_media_type.video %}
{% render 'video_player', element: element %}
{% endfor %}

Grouping: by_row

Groups elements by their row number.

{% for row in post.elements.by_row %}
<div class="row row-{{ row[0] }}">
{% for element in row[1] %}
{% render 'story_element', element: element %}
{% endfor %}
</div>
{% endfor %}

ElementDrop

Represents a single content element within a post. Elements use a unified media type with a media_type sub-field.

Properties

PropertyTypeDescription
typestringElement type: "rich_text" or "media"
media_typestringFor media: "image", "gallery", or "video"
slotstringLayout slot: "full", "left", or "right"
rowintegerRow number
positionintegerPosition within row
contentstringHTML content (for rich_text elements)
captionstringElement caption

Type Checking

MethodDescription
media?True if type is "media"
image?True if media type is "image"
gallery?True if media type is "gallery"
video?True if media type is "video"

Image Elements

PropertyTypeDescription
imageImageDropImage (use with image_url filter)
PropertyTypeDescription
gallery_imagesarrayArray of ImageDrop objects
gallery_stylestringLayout: "grid", "masonry", "carousel"
show_captionsbooleanWhether to display captions

Video Elements

PropertyTypeDescription
video_typestring"upload" or "embed"
video_urlstringVideo URL

Complete Example

{% for element in post.elements %}
{% case element.type %}
{% when 'rich_text' %}
<div class="prose">{{ element.content }}</div>

{% when 'media' %}
{% case element.media_type %}
{% when 'image' %}
{% if element.image %}
<figure>
<img src="{{ element.image | image_url: 'large' }}" alt="{{ element.image.alt }}">
{% if element.caption %}<figcaption>{{ element.caption }}</figcaption>{% endif %}
</figure>
{% endif %}

{% when 'gallery' %}
<div class="gallery gallery--{{ element.gallery_style }}">
{% for img in element.gallery_images %}
<img src="{{ img | image_url: 'medium' }}" alt="{{ img.alt }}">
{% endfor %}
</div>

{% when 'video' %}
{% if element.video_type == 'embed' %}
<div class="aspect-video">
<iframe src="{{ element.video_url }}" allowfullscreen></iframe>
</div>
{% else %}
<video src="{{ element.video_url }}" controls></video>
{% endif %}
{% endcase %}
{% endcase %}
{% endfor %}

TagDrop

Represents a content tag used for organizing posts.

PropertyTypeDescription
idintegerTag ID
namestringDisplay name
slugstringURL slug
urlstringTag page URL (e.g., /tags/skiing)
postsarrayApproved posts with this tag
posts_countintegerNumber of posts with this tag
<a href="{{ tag | tag_url }}" class="tag">
{{ tag.name }}
<span class="count">{{ tag.posts_count }}</span>
</a>

TagsCollectionDrop

A collection wrapper for post tags with weight-based accessors for intelligent tag selection. Tags are ordered by posts_count (most popular first), with normalized weights calculated relative to other tags on the same post.

Weight System

Each tag gets a weight from 0.0 to 1.0 based on its posts_count relative to the other tags on the post:

  • Weight 1.0 = highest posts_count (most broadly used tag)
  • Weight 0.0 = lowest posts_count (most specific/distinctive tag)

The tags are grouped into three tiers:

TierWeight RangeDescription
Dominant>= 0.66Broadly used tags with many posts
Moderate0.33 -- 0.66Middle-ground tags
Distinctive< 0.33Specific tags that differentiate this post

Singular Accessors (Return One Tag)

MethodReturnsDescription
dominantTagDropMost broadly used tag (weight near 1.0)
moderateTagDropMiddle-weight tag
distinctiveTagDropMost specific tag (weight near 0.0)
primaryTagDropAlias for distinctive -- recommended for display
firstTagDropSame as dominant
lastTagDropSame as distinctive

Plural Accessors (Return Arrays)

MethodReturnsDescription
dominantsarrayAll tags with weight >= 0.66
moderatesarrayAll tags with weight 0.33 -- 0.66
distinctivesarrayAll tags with weight < 0.33

Standard Methods

MethodReturnsDescription
sizeintegerNumber of tags
empty?booleanTrue if no tags
any?booleanTrue if has tags

When to Use Each Accessor

Use CaseAccessorWhy
Tag label on post cards.primary or .distinctiveMaximum differentiation between posts
Sidebar with natural grouping.moderateSome repetition but not overwhelming
Site-wide tag display.dominantBroadest classification
Tag cloud showing all tagsiterate .tags directlyShow everything
Filter out common tags.distinctivesOnly specific/niche tags

Examples

{%- comment -%} Show the most distinctive tag as a label {%- endcomment -%}
{% if post.tags.primary %}
<span class="tag-label">{{ post.tags.primary.name }}</span>
{% endif %}

{%- comment -%} Show the broadest/most common tag {%- endcomment -%}
{% if post.tags.dominant %}
<span class="site-section">{{ post.tags.dominant.name }}</span>
{% endif %}

{%- comment -%} All tags {%- endcomment -%}
{% for tag in post.tags %}
<a href="{{ tag | tag_url }}">{{ tag.name }}</a>
{% unless forloop.last %}, {% endunless %}
{% endfor %}

{%- comment -%} Only distinctive tags (most specific) {%- endcomment -%}
{% for tag in post.tags.distinctives %}
<a href="{{ tag | tag_url }}" class="tag tag--specific">{{ tag.name }}</a>
{% endfor %}

{%- comment -%} Check if post has any tags {%- endcomment -%}
{% if post.tags.any? %}
<div class="tags">{{ post.tags.size }} tags</div>
{% endif %}

PostsDrop

The global posts object provides direct access to individual posts by slug.

{%- comment -%} Access specific post by slug {%- endcomment -%}
{% assign featured = posts.welcome-to-our-site %}
{% if featured %}
<h2>{{ featured.title }}</h2>
<p>{{ featured.excerpt }}</p>
{% endif %}

{%- comment -%} Bracket notation for dynamic slugs {%- endcomment -%}
{% assign post_slug = section.settings.featured_post %}
{% assign post = posts[post_slug] %}
{% if post %}
<article>{{ post.title }}</article>
{% endif %}

CollectionDrop

Represents a collection of posts (Shopify-style). Collections can be manual (hand-picked posts) or smart (automatic based on rules).

PropertyTypeDescription
idintegerCollection ID
titlestringCollection title
slugstringURL slug
descriptionstringCollection description
collection_typestring"manual" or "smart"
imageImageDropCollection image
postsarrayPosts in this collection
posts_countintegerNumber of posts
urlstringCollection URL
published?booleanTrue if published

Collections support direct iteration (they delegate to their posts):

{%- comment -%} Direct iteration {%- endcomment -%}
{% for post in collections.featured %}
{{ post.title }}
{% endfor %}

{%- comment -%} Or via .posts {%- endcomment -%}
{% for post in collections.featured.posts %}
{{ post.title }}
{% endfor %}

CollectionsDrop

Global access to all collections (Shopify-style pattern).

MethodReturnsDescription
collections.slugCollectionDropAccess by slug (dot notation)
collections['slug']CollectionDropAccess by slug (bracket notation)
collections.all_postsarrayAll approved posts (recent first, limit 50)
collections.all_tagsarrayAll tags
{%- comment -%} Access collection by handle {%- endcomment -%}
{% for post in collections.featured %}
<article>{{ post.title }}</article>
{% endfor %}

{%- comment -%} Hyphenated slugs need bracket notation {%- endcomment -%}
{% assign news = collections['latest-news'] %}
{% for post in news.posts limit: 5 %}
<a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}

{%- comment -%} All posts shortcut {%- endcomment -%}
{% for post in collections.all_posts %}
{{ post.title }}
{% endfor %}

{%- comment -%} Iterate all collections {%- endcomment -%}
{% for collection in collections %}
<h3>{{ collection.title }} ({{ collection.posts_count }})</h3>
{% endfor %}

UserDrop

Represents a user/author.

PropertyTypeDescription
idintegerUser ID
first_namestringFirst name
last_namestringLast name
full_namestringFirst + last name
display_namestringUsername or full name
usernamestringUsername
initialsstringInitials for avatar placeholder
biostringBiography text
imageImageDropAvatar image
avatarImageDropAlias for image
urlstringProfile URL
posts_countintegerCount of published posts
member_sincestringFormatted join date (e.g., "January 2026")
<div class="author-card">
{% if user.image %}
<img src="{{ user.image | image_url: 'avatar' }}" alt="{{ user.full_name }}">
{% else %}
<div class="initials">{{ user.initials }}</div>
{% endif %}
<h3>{{ user.display_name }}</h3>
<p>{{ user.bio }}</p>
<span>{{ user.posts_count }} articles</span>
</div>

SectionDrop

The current section being rendered. Available in all section templates.

PropertyTypeDescription
idstringUnique section instance ID
settingsSectionSettingsDropSection settings
blocksBlocksCollectionDropSection blocks

section.settings

Access settings defined in the section's schema:

{% htmltag 'h2' section.settings.title class: 'section-title' %}

{%- comment -%} Or manually {%- endcomment -%}
<h2>{{ section.settings.title }}</h2>

For checkboxes, use natural boolean checks:

{% if section.settings.show_date %}
<time>{{ post.published_at | date: "%B %d, %Y" }}</time>
{% endif %}

For links, access properties directly:

<a href="{{ section.settings.button | link_url }}"
{% if section.settings.button | link_new_tab? %}target="_blank" rel="noopener"{% endif %}>
{{ section.settings.button | link_text }}
</a>

Helper methods on non-boolean settings:

MethodDescription
.valueRaw underlying value
.attrData attributes for element identification (optional)
.blank?True if nil or empty
.present?True if not blank

Note: .attr is optional -- it outputs data-setting and data-section-id attributes for element identification/debugging. Live preview works via server-side re-rendering regardless of whether .attr is used.

Boolean settings (checkboxes) return raw true/false values directly, so you can use natural boolean checks.

section.blocks

Blocks support type filtering via property access:

{%- comment -%} All blocks {%- endcomment -%}
{% for block in section.blocks %}
<div>{{ block.settings.title }}</div>
{% endfor %}

{%- comment -%} Only "feature" type blocks {%- endcomment -%}
{% for block in section.blocks.feature %}
<div class="feature-card">{{ block.settings.title }}</div>
{% endfor %}

Block properties:

PropertyTypeDescription
block.idstringUnique block ID
block.typestringBlock type from schema
block.settingsSectionSettingsDropBlock settings
block.blocksBlocksCollectionDropNested child blocks

PageDrop

The current page being rendered.

PropertyTypeDescription
idintegerPage ID
titlestringPage title
slugstringURL slug
urlstringFull URL path (/ for home, /slug for others)
canonical_urlstringSame as url
meta_titlestringSEO title
meta_descriptionstringSEO description
seo_titlestringResolved meta title with fallback
seo_descriptionstringResolved meta description
og_image_urlstringOpen Graph image URL
published?booleanTrue if published
system_page?booleanTrue if this is a system template page
<title>{{ page.seo_title | default: page.title }} | {{ site.name }}</title>
<meta name="description" content="{{ page.seo_description }}">
{% if page.og_image_url %}
<meta property="og:image" content="{{ page.og_image_url }}">
{% endif %}

SiteDrop

Global site configuration. Available as {{ site }}.

PropertyTypeDescription
idintegerSite ID
namestringSite name
subdomainstringSite subdomain
urlstringFull site URL

OAuth Methods

MethodReturnsDescription
oauth_login_enabled?booleanTrue if any OAuth provider is configured
enabled_oauth_providersarrayList of enabled provider names
<footer>
<p>&copy; {{ site.name }}</p>
</footer>

{%- comment -%} Show social login only if configured {%- endcomment -%}
{% if site.oauth_login_enabled? %}
<div class="social-login">
{% login_form "google" %}{% endlogin_form %}
</div>
{% endif %}

MemberDrop

Represents the currently logged-in member (site visitor). Available as {{ member }} when a member is signed in, nil otherwise.

Use with the {% member %} and {% guest %} conditional tags to show different content based on authentication state.

Properties

PropertyTypeDescription
idintegerUnique member ID
emailstringMember email address
first_namestringFirst name
last_namestringLast name
usernamestringUsername
biostringBiography text
avatar_urlstringURL to member's avatar image
display_namestringPreferred display name
full_namestringFull name (first + last)
member_sincedatetimeAccount creation date

Examples

{% member %}
<div class="member-profile">
{% if member.avatar_url %}
<img src="{{ member.avatar_url }}" alt="{{ member.display_name }}">
{% endif %}
<p>Welcome back, {{ member.display_name }}!</p>
<p>Member since {{ member.member_since | date: "%B %Y" }}</p>
</div>
{% endmember %}
  • member_logged_in -- Boolean, true when a member is signed in, false otherwise
  • {% member %} -- Conditional block tag

FlashDrop

Exposes flash messages to templates. Available as {{ flash }}. Flash messages are set by the authentication system after login, logout, registration, or errors.

Properties

PropertyTypeDescription
noticestringSuccess/info message
alertstringWarning message
errorstringError message

Any custom flash key is also accessible via {{ flash.custom_key }}.

Examples

{% if flash.notice %}
<div class="flash flash--notice">{{ flash.notice }}</div>
{% endif %}

{% if flash.alert %}
<div class="flash flash--alert">{{ flash.alert }}</div>
{% endif %}

{% if flash.error %}
<div class="flash flash--error">{{ flash.error }}</div>
{% endif %}

RoutesDrop

Provides URL paths for member authentication pages. Available as {{ routes }}.

Properties

PropertyTypeReturns
member_login_pathstring/member/login
member_logout_pathstring/member/logout
member_register_pathstring/member/register
member_password_pathstring/member/password/new

Examples

<nav class="auth-nav">
{% guest %}
<a href="{{ routes.member_login_path }}">Sign In</a>
<a href="{{ routes.member_register_path }}">Create Account</a>
{% endguest %}

{% member %}
<span>{{ member.display_name }}</span>
{% member_form "logout" %}
<button type="submit">Sign Out</button>
{% endmember_form %}
{% endmember %}
</nav>

RequestDrop

Request context for the current page. Available as {{ request }}.

Properties

PropertyTypeDescription
pathstringCurrent request path (e.g., /my-article)
localestringCurrent locale (e.g., en)

Examples

<html lang="{{ request.locale | default: 'en' }}">

{%- comment -%} Highlight active nav link {%- endcomment -%}
<a href="/about"
class="{% if request.path == '/about' %}active{% endif %}">
About
</a>

member_logged_in

A boolean variable (not a drop) available in all templates. Returns true when a member is signed in, false otherwise.

{% if member_logged_in %}
<p>Hello, {{ member.display_name }}</p>
{% else %}
<a href="{{ routes.member_login_path }}">Sign In</a>
{% endif %}
Use conditional tags instead

For cleaner templates, prefer the {% member %} and {% guest %} block tags over checking member_logged_in directly.


forloop

Standard Liquid object available inside {% for %} loops:

PropertyTypeDescription
forloop.indexintegerCurrent iteration (1-based)
forloop.index0integerCurrent iteration (0-based)
forloop.firstbooleanTrue on first iteration
forloop.lastbooleanTrue on last iteration
forloop.lengthintegerTotal iterations
{% for post in collection.posts %}
<article class="{% if forloop.first %}featured{% endif %}">
{{ post.title }}
</article>
{% endfor %}

  • Liquid Tags -- Custom tags like {% member_form %}, {% member %}, {% guest %}, {% post %}, and {% render %}
  • Liquid Filters -- Filters like image_url and post_url
  • Settings System -- Defining section and theme settings
  • Sections -- Building configurable page sections