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' }}
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
| Variable | Type | Description |
|---|---|---|
site | SiteDrop | Current site name, URL, and OAuth configuration |
settings | Hash | Resolved theme settings (from settings_data.json) |
typography | TypographyDrop | Baseline grid calculations from theme settings |
request | RequestDrop | Current request path and locale |
posts | PostsDrop | Access individual posts by slug |
tags | array | All tags ordered by name |
collections | CollectionsDrop | Access collections by slug or iterate all |
blogs | BlogsDrop | Access blogs by slug |
member | MemberDrop | Current authenticated member, or nil |
member_logged_in | boolean | true when a member is signed in |
flash | FlashDrop | Flash messages (notice, alert, error) |
routes | RoutesDrop | Member auth route paths |
Additional Variables in Section Templates
| Variable | Type | Description |
|---|---|---|
section | SectionDrop | Current section data, settings, and blocks |
Additional Variables in Layout Templates
| Variable | Type | Description |
|---|---|---|
page | PageDrop | Current page being rendered |
content_for_layout | string | Rendered section HTML |
layout_zones | Hash | Rendered HTML per layout zone |
content | Drop | Content being rendered (post, collection, tag, or page) |
content_type | string | "post", "page", "collection", or "tag" |
parents | array | Parent chain for breadcrumbs |
parent | Drop | Immediate 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:
| Property | Type | Description |
|---|---|---|
type | string | Content type identifier |
title | string | Display title |
slug | string | URL slug |
url | string | Full URL path |
canonical_url | string | Shortest URL path |
description | string | Meta description or teaser |
image | ImageDrop | Cover/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
| Property | Type | Description |
|---|---|---|
alt | string | Alt text for accessibility |
caption | string | Image caption |
width | integer | Width in pixels |
height | integer | Height in pixels |
aspect_ratio | float | Width divided by height |
filename | string | Original filename |
content_type | string | MIME type (e.g., "image/jpeg") |
attached? | boolean | True if image exists |
present? | boolean | Alias for attached? |
blank? | boolean | True 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
| Property | Type | Description |
|---|---|---|
id | integer | Unique post ID |
title | string | Post title |
slug | string | URL slug |
url | string | Full URL path (e.g., /my-post) |
canonical_url | string | Shortest URL path |
teaser | string | Short summary text |
excerpt | string | Auto-generated excerpt (200 chars) |
status | string | Post status |
published_at | datetime | Publication date |
created_at | datetime | Creation date |
updated_at | datetime | Last update date |
Display Helpers
| Property | Type | Description |
|---|---|---|
published_date | string | Localized date (e.g., "January 6, 2026") |
published_time_ago | string | Relative time (e.g., "2 days ago") |
reading_time | string | Estimated read time (e.g., "5 min read") |
Images
| Property | Type | Description |
|---|---|---|
image | ImageDrop | Cover image |
cover_image | ImageDrop | Alias for image |
images | array | First gallery's images |
galleries | array | All 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
| Property | Type | Description |
|---|---|---|
author | UserDrop | Post author |
author.full_name | string | Full display name |
author.display_name | string | Username or full name |
author.image | ImageDrop | Avatar image |
author.bio | string | Biography text |
<div class="author">
<img src="{{ post.author.image | image_url: 'avatar' }}" alt="">
<span>{{ post.author.display_name }}</span>
</div>
Tags
| Property | Type | Description |
|---|---|---|
tags | TagsCollectionDrop | All 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)
| Property | Type | Description |
|---|---|---|
elements | ElementsCollectionDrop | All content elements |
has_elements | boolean | True if post has elements |
texts | array | All 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:
| Method | Returns | Description |
|---|---|---|
elements.left | array | Left slot elements (50% width) |
elements.right | array | Right slot elements (50% width) |
elements.full | array | Full-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
| Property | Type | Description |
|---|---|---|
type | string | Element type: "rich_text" or "media" |
media_type | string | For media: "image", "gallery", or "video" |
slot | string | Layout slot: "full", "left", or "right" |
row | integer | Row number |
position | integer | Position within row |
content | string | HTML content (for rich_text elements) |
caption | string | Element caption |
Type Checking
| Method | Description |
|---|---|
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
| Property | Type | Description |
|---|---|---|
image | ImageDrop | Image (use with image_url filter) |
Gallery Elements
| Property | Type | Description |
|---|---|---|
gallery_images | array | Array of ImageDrop objects |
gallery_style | string | Layout: "grid", "masonry", "carousel" |
show_captions | boolean | Whether to display captions |
Video Elements
| Property | Type | Description |
|---|---|---|
video_type | string | "upload" or "embed" |
video_url | string | Video 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.
| Property | Type | Description |
|---|---|---|
id | integer | Tag ID |
name | string | Display name |
slug | string | URL slug |
url | string | Tag page URL (e.g., /tags/skiing) |
posts | array | Approved posts with this tag |
posts_count | integer | Number 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:
| Tier | Weight Range | Description |
|---|---|---|
| Dominant | >= 0.66 | Broadly used tags with many posts |
| Moderate | 0.33 -- 0.66 | Middle-ground tags |
| Distinctive | < 0.33 | Specific tags that differentiate this post |
Singular Accessors (Return One Tag)
| Method | Returns | Description |
|---|---|---|
dominant | TagDrop | Most broadly used tag (weight near 1.0) |
moderate | TagDrop | Middle-weight tag |
distinctive | TagDrop | Most specific tag (weight near 0.0) |
primary | TagDrop | Alias for distinctive -- recommended for display |
first | TagDrop | Same as dominant |
last | TagDrop | Same as distinctive |
Plural Accessors (Return Arrays)
| Method | Returns | Description |
|---|---|---|
dominants | array | All tags with weight >= 0.66 |
moderates | array | All tags with weight 0.33 -- 0.66 |
distinctives | array | All tags with weight < 0.33 |
Standard Methods
| Method | Returns | Description |
|---|---|---|
size | integer | Number of tags |
empty? | boolean | True if no tags |
any? | boolean | True if has tags |
When to Use Each Accessor
| Use Case | Accessor | Why |
|---|---|---|
| Tag label on post cards | .primary or .distinctive | Maximum differentiation between posts |
| Sidebar with natural grouping | .moderate | Some repetition but not overwhelming |
| Site-wide tag display | .dominant | Broadest classification |
| Tag cloud showing all tags | iterate .tags directly | Show everything |
| Filter out common tags | .distinctives | Only 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).
| Property | Type | Description |
|---|---|---|
id | integer | Collection ID |
title | string | Collection title |
slug | string | URL slug |
description | string | Collection description |
collection_type | string | "manual" or "smart" |
image | ImageDrop | Collection image |
posts | array | Posts in this collection |
posts_count | integer | Number of posts |
url | string | Collection URL |
published? | boolean | True 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).
| Method | Returns | Description |
|---|---|---|
collections.slug | CollectionDrop | Access by slug (dot notation) |
collections['slug'] | CollectionDrop | Access by slug (bracket notation) |
collections.all_posts | array | All approved posts (recent first, limit 50) |
collections.all_tags | array | All 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.
| Property | Type | Description |
|---|---|---|
id | integer | User ID |
first_name | string | First name |
last_name | string | Last name |
full_name | string | First + last name |
display_name | string | Username or full name |
username | string | Username |
initials | string | Initials for avatar placeholder |
bio | string | Biography text |
image | ImageDrop | Avatar image |
avatar | ImageDrop | Alias for image |
url | string | Profile URL |
posts_count | integer | Count of published posts |
member_since | string | Formatted 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.
| Property | Type | Description |
|---|---|---|
id | string | Unique section instance ID |
settings | SectionSettingsDrop | Section settings |
blocks | BlocksCollectionDrop | Section 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:
| Method | Description |
|---|---|
.value | Raw underlying value |
.attr | Data 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:
| Property | Type | Description |
|---|---|---|
block.id | string | Unique block ID |
block.type | string | Block type from schema |
block.settings | SectionSettingsDrop | Block settings |
block.blocks | BlocksCollectionDrop | Nested child blocks |
PageDrop
The current page being rendered.
| Property | Type | Description |
|---|---|---|
id | integer | Page ID |
title | string | Page title |
slug | string | URL slug |
url | string | Full URL path (/ for home, /slug for others) |
canonical_url | string | Same as url |
meta_title | string | SEO title |
meta_description | string | SEO description |
seo_title | string | Resolved meta title with fallback |
seo_description | string | Resolved meta description |
og_image_url | string | Open Graph image URL |
published? | boolean | True if published |
system_page? | boolean | True 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 }}.
| Property | Type | Description |
|---|---|---|
id | integer | Site ID |
name | string | Site name |
subdomain | string | Site subdomain |
url | string | Full site URL |
OAuth Methods
| Method | Returns | Description |
|---|---|---|
oauth_login_enabled? | boolean | True if any OAuth provider is configured |
enabled_oauth_providers | array | List of enabled provider names |
<footer>
<p>© {{ 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
| Property | Type | Description |
|---|---|---|
id | integer | Unique member ID |
email | string | Member email address |
first_name | string | First name |
last_name | string | Last name |
username | string | Username |
bio | string | Biography text |
avatar_url | string | URL to member's avatar image |
display_name | string | Preferred display name |
full_name | string | Full name (first + last) |
member_since | datetime | Account 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 %}
Related
member_logged_in-- Boolean,truewhen a member is signed in,falseotherwise{% 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
| Property | Type | Description |
|---|---|---|
notice | string | Success/info message |
alert | string | Warning message |
error | string | Error 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
| Property | Type | Returns |
|---|---|---|
member_login_path | string | /member/login |
member_logout_path | string | /member/logout |
member_register_path | string | /member/register |
member_password_path | string | /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
| Property | Type | Description |
|---|---|---|
path | string | Current request path (e.g., /my-article) |
locale | string | Current 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 %}
For cleaner templates, prefer the {% member %} and {% guest %} block tags over checking member_logged_in directly.
forloop
Standard Liquid object available inside {% for %} loops:
| Property | Type | Description |
|---|---|---|
forloop.index | integer | Current iteration (1-based) |
forloop.index0 | integer | Current iteration (0-based) |
forloop.first | boolean | True on first iteration |
forloop.last | boolean | True on last iteration |
forloop.length | integer | Total iterations |
{% for post in collection.posts %}
<article class="{% if forloop.first %}featured{% endif %}">
{{ post.title }}
</article>
{% endfor %}
Related Documentation
- Liquid Tags -- Custom tags like
{% member_form %},{% member %},{% guest %},{% post %}, and{% render %} - Liquid Filters -- Filters like
image_urlandpost_url - Settings System -- Defining section and theme settings
- Sections -- Building configurable page sections