Liquid Template Reference

All variables, tags, and filters available in Cobber's page and email template editor. Use the search above to find anything quickly.

Global Variables

Available in every page template and snippet.

organization OrganisationDrop

Organisation branding, contact info, social proof counts, and membership types.

Variable Type Description Example
organization.name String Organisation name Example Org
organization.subdomain String URL subdomain exampleorg
organization.primary_public_host String Public host for the primary website (connected domain, or Cobber subdomain before go-live) example.org.au
organization.phone String Organisation phone number (07) 3000 0000
organization.address String Organisation street address 123 Main St, Sydney NSW 2000
organization.primary_color String Primary brand colour (hex) #1E3A5F
organization.secondary_color String Secondary brand colour (hex) #2D4A6F
organization.accent_color String Accent brand colour (hex) #C4956A
organization.font_family String Body font from the active theme (alias of theme.body_font) Instrument Sans
organization.logo_attached Boolean Whether a logo has been uploaded true
organization.logo_url String Path to the organisation logo file /rails/active_storage/blobs/...
organization.logo_link_url String URL the logo links to (defaults to "/") /
organization.copyright_name String Name used in copyright notices Example Org Inc.
organization.country String ISO country code for Stripe billing (AU or NZ) AU
organization.currency String ISO currency code for Stripe charges (AUD or NZD) AUD
organization.authorization_text String Authorised by text for political ads Authorised by J. Smith, Sydney
organization.header_theme String Header colour theme (light or dark) dark
organization.header_layout String Header layout variant centered
organization.nav_items Array Navigation items. Each item: url, label, style, button_color, children (nested array). see nav_items below
organization.member_count Integer People who hold a current (unexpired or lifetime) membership. Matches the Members figure on the dashboard's People page. A couple membership counts both partners, so this is people, not membership records 423
organization.volunteer_count Integer People marked as a volunteer at any level — interested, active, core team or inactive. Matches the Volunteers figure on the dashboard's People page 87
organization.active_volunteer_count Integer Only the volunteers whose status is Active — the people currently doing volunteer work 12
organization.supporter_count Integer Every real contact in the database: not anonymous, not deleted, and with an email address on file. The broadest number available — it includes members, volunteers, donors and anyone who ever filled in a form 1842
organization.total_raised Float Total amount raised from all donations ($) 48320.5
organization.donor_count Integer Number of unique donors 312
organization.paid_membership_types Array All active paid membership tiers (recurring first). Each item: see MembershipType below.
organization.recurring_membership_types Array Recurring (subscription) membership tiers only.
organization.one_time_membership_types Array One-time payment membership tiers only.

MembershipType fields (each item in paid_membership_types etc.)

Variable Type Description Example
.id Integer Membership type ID 3
.name String Tier name Full Member
.price Decimal Price in dollars (raw) 50.0
.display_price String Formatted price string $50
.recurring Boolean True if this is a recurring/subscription tier true
.one_time Boolean True if this is a one-time payment false
.period_description String Billing period description per year
.couple_membership Boolean True if this tier covers two people false
.form_url String URL to the membership signup form for this tier /membership_signups/form?membership_type_id=3&landing=true

nav_items fields (each item in organization.nav_items)

Variable Type Description Example
.url String Resolved URL (page slug or custom URL) /about-us
.label String Link label text About
.style String Display style (link or button) button
.button_color String Button colour override (hex or CSS class) #C4956A
.children Array Nested sub-navigation items (same fields)

Example — rendering nav

{% for item in organization.nav_items %}
  <a href="{{ item.url }}"
     class="{% if item.style == 'button' %}btn{% endif %}">
    {{ item.label }}
  </a>
{% endfor %}

Example — membership type grid

{% for type in organization.paid_membership_types %}
  <a href="{{ type.form_url }}">
    {{ type.name }} — {{ type.display_price }}
    {% if type.recurring %} / {{ type.period_description }}{% endif %}
  </a>
{% endfor %}

page PageDrop

The current page. Also available as widget_page inside {% widget %} partials.

Variable Type Description Example
page.id Integer Page ID 42
page.title String Page title Sign our petition
page.slug String URL slug sign-our-petition
page.url String Absolute path to this page /sign-our-petition
page.template_type String Page type (petition, volunteer, contact, etc.) petition
page.published? Boolean Whether the page is publicly visible true
page.content String Rich text content set in the page editor
page.created_at String Creation date (DD MMMM YYYY) 01 January 2025
page.updated_at String Last updated date (DD MMMM YYYY) 15 March 2025
page.meta_title String SEO meta title (falls back to page.title) Stop the development
page.meta_description String SEO meta description Join thousands calling for change.
page.meta_image_url String URL for the meta/OG image
page.og_title String Open Graph title (falls back to meta_title)
page.og_description String Open Graph description (falls back to meta_description)
page.og_image_url String Open Graph image URL
page.twitter_title String Twitter card title (falls back to og_title)
page.twitter_description String Twitter card description
page.twitter_image_url String Twitter card image URL
page.facebook_share_text String Pre-filled text for Facebook share button I just signed — you should too!
page.twitter_share_text String Pre-filled text for Twitter/X share button Sign the petition: {url}
page.email_share_subject String Subject line for email share Please sign this petition
page.email_share_body String Body text for email share I thought you'd want to sign...
page.image_url String Main page image URL. Resolves the media library item keyed "page_image", else a manually entered URL. /rails/active_storage/blobs/...
page.media Array The page's file library — any number of uploaded images, videos and documents (PDF, Word, Excel, CSV). Look up by key (page.media["hero_banner"]) or loop over it. Each item: see fields below.
page.media.images Array Only the image items in the file library.
page.media.videos Array Only the video items in the file library.
page.media.documents Array Only the documents — PDFs, Word files and spreadsheets. Link to these rather than rendering them inline.
page.template_data Hash All page template fields as a hash (same as page.custom_fields). Useful for inspecting or iterating all fields.
page.custom_fields Hash Alias for page.template_data. Returns the full hash — not used for dot-access to individual fields.
page.KEY String Access any template field directly by its key via method_missing — e.g. {{ page.hero_title }}, {{ page.petition_goal }}. This is the correct way to use page template variables. page.petition_title → "Stop the development"
page.survey Drop Survey question tree on survey pages — sections, questions, and rendered inputs. {% for section in page.survey.sections %}
page.survey.sections Array Ordered survey sections.
page.survey.questions Array All questions flattened across sections.
page.survey.section_count Integer Number of sections. 3
page.survey.question_count Integer Number of questions. 12

page.survey SurveyDrop

On survey pages, iterate sections and questions instead of hardcoding fields. Each question exposes label_tag and input HTML that posts as answers[question_id].

Variable Type Description Example
section.title String Section heading Your priorities
section.description String Optional section intro
section.questions Array Questions in this section
section.question_count Integer Number of questions in the section 4
Variable Type Description Example
question.label String Question text What matters most?
question.help_text String Optional help under the label
question.question_type String short_text | long_text | radio | checkboxes radio
question.required? Boolean Whether an answer is required true
question.label_tag HTML Rendered <label> element
question.input HTML Rendered input / textarea / radios / checkboxes named answers[id]
question.options Array Choice options with id and label (radio/checkboxes)

Example — render every question

{% for section in page.survey.sections %}
  <h2>{{ section.title }}</h2>
  {% for question in section.questions %}
    {{ question.label_tag }}
    {{ question.input }}
  {% endfor %}
{% endfor %}

page.media PageMediaDrop

The page's file library — any number of uploaded images, videos and documents (PDF, Word, Excel, CSV). Look one up by key (page.media["hero_banner"]) or loop over the collection; page.media.images, page.media.videos and page.media.documents filter by type. Each item exposes:

Variable Type Description Example
.url String Absolute URL to the file /rails/active_storage/blobs/...
.file_name String Original uploaded file name hero.jpg
.content_type String MIME type image/jpeg
.key String Author-assigned key used for lookup hero_banner
.caption String Optional caption Rally in Brisbane
.alt_text String Alt text for images, or link text for documents Read the platform
.image? Boolean True if the file is an image true
.video? Boolean True if the file is a video false
.document? Boolean True for PDFs, Word files and spreadsheets true
.extension String Upper-cased file extension — handy for labelling a download link PDF
.file_size Integer File size in bytes 1887436
.file_size_human String File size ready to display next to a link 1.8 MB
.width Integer Image width in pixels (images only) 1200
.height Integer Image height in pixels (images only) 630

Example — look up one image by key

<img src="{{ page.media["hero_banner"].url }}" alt="{{ page.media["hero_banner"].alt_text }}">

Example — loop over every image

{% for img in page.media.images %}
  <img src="{{ img.url }}" alt="{{ img.alt_text }}">
{% endfor %}

Example — list every document as a download link

{% for doc in page.media.documents %}
  <a href="{{ doc.url }}">{{ doc.alt_text | default: doc.file_name }} ({{ doc.extension }}, {{ doc.file_size_human }})</a>
{% endfor %}

Example — link one PDF by key

<a href="{{ page.media["platform_pdf"].url }}">Read our policy platform</a>

theme ThemeDrop

The active theme's colour and font settings.

Variable Type Description Example
theme.name String Theme name Standard
theme.primary_color String Primary colour (hex) #1E3A5F
theme.secondary_color String Secondary colour (hex) #2D4A6F
theme.accent_color String Accent colour (hex) #C4956A
theme.heading_font String Heading font family Instrument Sans
theme.body_font String Body font family Source Sans 3
theme.active Boolean Whether this is the active theme true

System Variables

Variable Type Description Example
current_year Integer Current calendar year. Useful for copyright lines. 2025
has_submitted String Set to "true" after the page form is successfully submitted. Use to show a thank-you state. true
organization_logo_html HTML Pre-rendered organisation logo image tag, linked to logo_link_url. Output with {{ organization_logo_html }}. <a href="/"><img src="..." alt="..."></a>

Form & Fields

Use page.form to render forms in your templates. Every page type exposes its own set of fields. Never write a literal <form> tag — use page.form.open instead.

Form structure

Tag / VariableDescription
{{ page.form.open }} Renders the opening <form> tag with CSRF token, page ID, submission type, and the Cloudflare Turnstile challenge when one is configured. Always place at the start of a form. Never write a literal <form> tag for a page form: a hand-rolled one with hand-written hidden fields renders and submits, then is rejected with a bare 422.
{{ widget_page.form.open }} The same, inside a widget partial — opens the embedded page's form, with the embedded page's ID and submission type. Use this, not page.form.open, inside a widget partial: page is the host page there, and its form would post the wrong page ID and type.
{{ page.form.close }} Renders the closing </form> tag.
{{ page.form.submit }} Renders the submit button. Button text comes from the page's submit_button_text, continue_button_text, or signup_button_text template variable (in that order).

Field methods

Replace FIELD with a field name from the table below (e.g. page.form.email.input).

VariableDescription
page.form.FIELD.label Renders the <label> element for this field.
page.form.FIELD.input Renders the appropriate input element (input, textarea, select, or checkbox) for this field. Apply form filters to customise.
page.form.FIELD.name The field's input name attribute value.
page.form.FIELD.type The field type (text, email, tel, textarea, select, checkbox, checkbox_group, date).
page.form.FIELD.required Boolean — whether this field is required.

Person custom field methods

Define fields under Settings → Custom Fields (People), then render them with page.person_custom_fields. Replace FIELD with the field key (e.g. page.person_custom_fields.financial_member.input). Loop a group with {% for field in page.person_custom_fields["Donor compliance"] %}.

VariableDescription
page.person_custom_fields.FIELD.label Renders the <label> element for this person custom field (unstyled — apply add_class in the template).
page.person_custom_fields.FIELD.input Renders the input, textarea, select, or checkbox for this field. Submitted as person_custom_fields[field_key]. For booleans, also output field.hidden_input before styling field.input.
page.person_custom_fields.FIELD.key The field_key defined in Settings → Custom Fields (People).
page.person_custom_fields.FIELD.type Field type: text, textarea, select, boolean, number, or url.
page.person_custom_fields.FIELD.group The custom field group name this field belongs to.
page.person_custom_fields.FIELD.instructions Help text configured on the field definition.
page.person_custom_fields.FIELD.required Boolean — whether this field is required.
page.person_custom_fields["Group name"] Returns fields in a group for looping, e.g. {% for field in page.person_custom_fields["Donor compliance"] %}.

Available fields by page type

Page TypeAvailable Fields
contact enquiry_type first_name last_name email phone message
petition first_name last_name email city display_name
volunteer first_name last_name email mobile date_of_birth
signup first_name last_name email phone interests
home email
donation first_name last_name email phone amount address_line1 suburb state postcode
membership first_name last_name email phone membership_type address_line1 suburb state postcode

Special form tags

Use these inside {{ page.form.open }}{{ page.form.close }} for complex input types.

TagDescription
{% form_address %} Renders a full Australian address block (suburb, postcode, state) with AddressFinder autocomplete. Params: prefix: "residential" or "mailing", required: true/false, manual_entry: true/false, fields: postcode (postcode-only).
{% form_interests %} Renders a checkbox grid of volunteer/interest options from the page's template data. Params: name: "volunteer_interests", columns: 2.
{% issues_form %} Renders an issues/preferences checkbox grid (suitable for contact pages). Params: columns: 2.

Example — petition form

{{ page.form.open }}
  <div class="grid grid-cols-2 gap-4">
    <div>
      {{ page.form.first_name.label }}
      {{ page.form.first_name.input | add_class: "w-full border rounded px-3 py-2" | placeholder: "First name" }}
    </div>
    <div>
      {{ page.form.last_name.label }}
      {{ page.form.last_name.input | add_class: "w-full border rounded px-3 py-2" }}
    </div>
  </div>
  {{ page.form.email.label }}
  {{ page.form.email.input | add_class: "w-full border rounded px-3 py-2" | placeholder: "you@example.com" }}
  {% form_address %}
  {{ page.form.submit }}
{{ page.form.close }}

Page-Type Variables

These fields are set in the page editor. Access them as {{ page.KEY }} — e.g. {{ page.petition_title }}. page.custom_fields returns the full template data hash if you need to inspect it, but individual fields use direct dot-access.

Home pages home

VariableLabelTypeNotes
page.hero_title Hero Title String
page.hero_subtitle Hero Subtitle String
page.hero_button_text Hero Button Text String
page.about_section About Section HTML
page.email_placeholder Email Placeholder String
page.signup_button_text Signup Button Text String

Petition pages petition

VariableLabelTypeNotes
page.petition_title Petition Title String
page.petition_description Petition Description String
page.image_url Page Image URL String Main page image — media library "page_image" item, or a manually entered URL
page.main_content Main Content HTML The issue details / body copy
page.initial_signatures Initial Signatures Integer Starting count added to online signatures
page.petition_goal Petition Goal Integer Target number for page.signatures_percentage
page.submit_button_text Submit Button Text String
page.social_proof_enabled Social Proof Enabled String Set to "1" to show social proof notifications

Volunteer pages volunteer

VariableLabelTypeNotes
page.page_title Page Title String
page.hero_text Hero Text String
page.main_content Main Content HTML
page.intro_text Intro Text HTML Fallback if main_content is not set
page.form_title Form Title String
page.submit_button_text Submit Button Text String
page.thank_you_message Thank You Message String

Contact pages contact

VariableLabelTypeNotes
page.page_title Page Title String
page.intro_text Intro Text HTML
page.office_address Office Address String
page.phone_number Phone Number String
page.email_address Email Address String
page.enquiry_types Enquiry Types String Comma-separated list, e.g. "General,Media,Volunteering"
page.thank_you_message Thank You Message String
page.consent_text Consent Text String
page.submit_button_text Submit Button Text String

Signup pages signup

VariableLabelTypeNotes
page.page_title Page Title String
page.main_content Main Content HTML
page.form_title Form Title String
page.form_intro Form Introduction HTML
page.submit_button_text Submit Button Text String
page.social_proof_enabled Social Proof String Set to "1" to enable

Membership pages membership

VariableLabelTypeNotes
page.page_title Page Title String
page.hero_text Hero Text String
page.main_content Main Content HTML
page.benefits_title Benefits Title String
page.benefits_content Benefits Content HTML
page.submit_button_text Submit Button Text String
page.social_proof_enabled Social Proof String Set to "1" to enable

Donation pages donation

VariableLabelTypeNotes
page.page_title Page Title String
page.hero_text Hero Text String
page.main_content Main Content HTML
page.secure_text Secure Payment Text String
page.donation_amounts Donation Amounts String Comma-separated, e.g. "25,50,100,250"
page.default_amount Default Amount String
page.submit_button_text Submit Button Text String
page.processing_text Processing Text String
page.social_proof_enabled Social Proof String Set to "1" to enable

Post index pages post_index

VariableLabelTypeNotes
page.intro_text Intro Text HTML Lead paragraph under the feed title
page.eyebrow Eyebrow String Small label above the title (defaults to "Latest updates")

Post pages post

VariableLabelTypeNotes
page.body Body HTML The article body (rich text)
page.excerpt Excerpt String Short summary shown on the feed index cards
page.author Author String Byline shown on the article and cards

Directory pages directory

VariableLabelTypeNotes
page.page_title Page Title String
page.intro_text Intro Text HTML Lead paragraph under the title
page.page.branches Branches Array Included branches, ordered state then name. Loop with {% for branch in page.branches %}. Each is a branch (fields below).
page.page.branches_by_state Branches by State Array Groups of { state, anchor, branches } for state headings + anchor nav.
page.page.upcoming_events Upcoming Events Array Date-sorted events across all included branches. Each item: { event (EventDrop), branch (name), branch_url }.

Branch pages branch

VariableLabelTypeNotes
page.branch.name Branch Name String
page.branch.url Branch URL String Path to the branch's page under the directory, or nil if no published directory lists it
page.branch.state State String Parent state division name
page.branch.about About HTML Paragraph-formatted about text
page.branch.contact_email Contact Email String Public email (falls back to reply-to)
page.branch.electorates Electorates Array Federal electorates covered (strings)
page.branch.boundary_filters Boundary Filters Hash federal_electorate / state_electorate / lga / postcode lists
page.branch.member_count Member Count Integer Approx. people in the branch
page.branch.committee Committee Array Members — each has .name, .title, .has_title
page.branch.upcoming_events Upcoming Events Array Published upcoming events (each an EventDrop)

Social Proof Variables

Page-level counters and recent-activity lists. Also available as widget_page.signatures etc. inside {% widget %} partials.

Variable Type Description Example
page.submission_count Integer Total form submissions on this page (all types: petition, volunteer, signup, contact) 1247
page.signatures Integer Petition: total signatures (online actions + initial_signatures template variable) 842
page.signatures_percentage Integer Petition: percentage of petition_goal reached (0–100, capped at 100) 68
page.recent_signers Array Petition: up to 10 most recent signers. Each item: name, first_name, suburb, time_ago, signed_at.
page.recent_volunteers Array Volunteer: up to 10 most recent volunteer signups. Each item: name, first_name, suburb, time_ago, signed_up_at.
page.total_raised Float Donation: total amount raised via this specific page ($) 12480.0
page.donor_count Integer Donation: number of unique donors via this page 94

recent_signers fields

Variable Type Description Example
.name String Full display name Jane S.
.first_name String First name only Jane
.suburb String Suburb Toowong
.time_ago String Human-readable relative time 3 minutes ago
.signed_at String Formatted date (DD Mon YYYY) 15 Mar 2025

recent_volunteers fields

Variable Type Description Example
.name String Full display name James T.
.first_name String First name only James
.suburb String Suburb Fortitude Valley
.time_ago String Human-readable relative time 2 hours ago
.signed_up_at String Formatted date (DD Mon YYYY) 20 Mar 2025

Example — petition progress bar

{% assign pct = page.signatures_percentage %}
<div class="progress-bar" style="width: {{ pct }}%"></div>
<p>{{ page.signatures }} of {{ page.custom_fields.petition_goal }} signatures</p>

Example — recent signers list

{% for signer in page.recent_signers limit: 5 %}
  <p>{{ signer.first_name }} from {{ signer.suburb }} signed {{ signer.time_ago }} ago</p>
{% endfor %}

Posts & Feeds

A feed is a post_index page (e.g. "News" or "Media Releases"); a post is an article within it. On a feed template, page.posts is the paginated list of published posts. On an article template, the article fields are on page.*.

Variable Type Description Example
page.posts Array Feed (post_index) only: the feed's published posts for the current page, newest first. Loop with {% for post in page.posts %}. Each item: see fields below.
page.posts.total_count Integer Total published posts across all pages of this feed. 23
page.posts.first Post The most recent post (useful for a lead/feature card).
page.posts.pagination Object Pagination state for building a pager. Fields below.
page.hero_image_url String Post (article) page: URL of the featured image (hero_image attachment). /rails/active_storage/blobs/...
page.published_at String Post (article) page: editorial publish date (D Month YYYY). 11 June 2026
page.body HTML Post (article) page: the rich-text article body. <p>…</p>
page.author String Post (article) page: byline/author. Press Office
page.excerpt String Post (article) page: short summary shown on feed cards. A short summary.

post fields (inside {% for post in page.posts %})

Variable Type Description Example
.title String Post title Budget leaves regions behind
.url String Flat-slug path to the article /budget-response
.excerpt String Short summary for the card A short summary.
.author String Byline / author Press Office
.published_at String Editorial date (D Month YYYY) 11 June 2026
.featured_image String Featured image URL (hero_image; may be blank) /rails/active_storage/blobs/...
.body HTML Full article body <p>…</p>

page.posts.pagination fields

Variable Type Description Example
.current_page Integer Current page number (from ?page=N) 1
.total_pages Integer Total number of pages 3
.total_count Integer Total published posts in the feed 23
.previous_url String URL of the previous page (blank on page 1) /news
.next_url String URL of the next page (blank on last page) /news?page=2
.pages Array Numbered entries for a custom pager. Each: number, url, current.

Example — feed index with cards and pager

{% for post in page.posts %}
  <a href="{{ post.url }}">
    <img src="{{ post.featured_image }}">
    <h3>{{ post.title }}</h3>
    <p>{{ post.excerpt }}</p>
    <span>{{ post.published_at }} · {{ post.author }}</span>
  </a>
{% endfor %}

{% assign pg = page.posts.pagination %}
{% if pg.total_pages > 1 %}
  {% for p in pg.pages %}
    <a href="{{ p.url }}">{{ p.number }}</a>
  {% endfor %}
{% endif %}

Collections

A collection is a content type the organisation defines itself — candidates, policies, media appearances — with a listing page and a page per item. Its fields are set up under Settings → Website → Collections, and each one is addressable in Liquid by its key.

Templates are collection_index and collection_item, with an optional per-collection override using a suffixed key — collection_item.candidates — for a type that needs its own layout.

Variable Type Description Example
collections.<handle> Array Every published item in a collection, addressable from ANY template by the collection's handle (its plural name). Loop with {% for c in collections.candidates %}.
page.items Array Collection index only: this collection's published items for the current page, paginated. Same shape as page.posts.
page.items.total_count Integer Total published items across all pages. 24
page.items.first Item The first item (useful for a feature card).
page.items.pagination Object Pagination state — same fields as page.posts.pagination.

item fields (on collection_item, and inside {% for %})

Variable Type Description Example
item.title String Item title Felicity Cole
item.url String Path to the item page (index page slug + item slug) /team/felicity-cole
item.slug String The item's own slug felicity-cole
item.published_at String Editorial date (D Month YYYY) 11 June 2026
item.<field_key> Varies Any field defined on the collection, by its key. A reference field hands back the record it points at, so you can walk into it: {{ item.branch.name }}. {{ item.electorate }}
item.fields Hash All field values as a hash — for iterating fields the template does not know in advance. {{ item.fields.summary }}

Example — index cards, then a detail page that knows about the CRM

{%- comment -%} collection_index.candidates {%- endcomment -%}
{% for item in page.items %}
  <a href="{{ item.url }}">{{ item.title }} — {{ item.electorate }}</a>
{% endfor %}

{%- comment -%} collection_item.candidates {%- endcomment -%}
<h1>{{ item.title }}</h1>
<img src="{{ item.headshot }}">

{%- comment -%} A reference field hands back the record itself {%- endcomment -%}
<p>Your local branch: {{ item.branch.name }}</p>
{% for e in item.branch.upcoming_events limit: 2 %}
  {{ e.title }} — {{ e.date }}
{% endfor %}

{%- comment -%} Submissions land in the CRM against this candidate {%- endcomment -%}
{% widget 'contact' with 'contact_partial', attribute_to: item %}
Attribution: adding attribute_to: item to a {% widget %} tags every enquiry and donation with that item, so "everyone who contacted this candidate" is a filter in the CRM rather than a mailbox someone forwards.

Events

The events_index template renders the public events listing at /events, and the event template renders each individual event page. Both are edited in the Theme Editor like any other page layout — you can't create new pages of these types, but you can fully restyle them. On the event template the current event's fields are available on event.*.

Events index: loop over the events collection to lay out the listing however you like. Event page: use {{ event.* }} for the details, and drop in {% event_rsvp_form %} to render the registration, ticket selection and Stripe payment panel.

events collection (on the events_index template)

Variable Type Description Example
events.grouped_upcoming Array Upcoming events grouped by date for a timeline. Each group: date_label, day_label, events (array of EventDrop). Events already underway lead the list in a "Happening now" group.
events.upcoming Array Flat list of current and upcoming events (each an EventDrop).
events.has_upcoming Boolean Whether there are any upcoming events. true
events.in_progress Array Events currently underway (each an EventDrop).
events.has_in_progress Boolean Whether any event is currently underway. false
events.past Array Recent past events (each an EventDrop).
events.has_past Boolean Whether there are any past events. false
events.calendar Object Current-month calendar: month_label, weekday_labels, and weeks (each a row of day cells: day, in_month, today, has_event).

Example — events index timeline

{% for group in events.grouped_upcoming %}
  <h2>{{ group.date_label }} · {{ group.day_label }}</h2>
  {% for event in group.events %}
    <a href="{{ event.url }}">
      <h3>{{ event.title }}</h3>
      <p>{{ event.start_time }} · {{ event.location }}</p>
      <span>{{ event.capacity_label }}</span>
    </a>
  {% endfor %}
{% endfor %}

event fields (on the event template)

Variable Type Description Example
event.title String Event title Annual Fundraising Gala
event.event_type_display String Human-readable event type Fundraiser
event.date String Long date Monday, July 20, 2026
event.start_time String Start time 6:30 PM
event.end_time String End time (blank if none) 10:00 PM
event.time_range String Combined start–end time 6:30 PM - 10:00 PM
event.description HTML Paragraph-formatted event description <p>Our biggest night…</p>
event.online Boolean Whether the event is online false
event.online_url String Join URL for online events https://zoom.us/j/…
event.venue_name String Venue name Brisbane Convention Centre
event.full_address String Full venue address Merivale St, South Brisbane, QLD, 4101
event.location String Display location (venue + suburb, or Online Event) Brisbane Convention Centre, South Brisbane
event.directions_url String Google Maps directions URL (blank when online) https://maps.google.com/?q=…
event.cover_image_url String Cover image URL (blank when none uploaded) /rails/active_storage/blobs/…
event.share_url String Absolute URL to the event page (for share links) https://example.org.au/events/gala
event.meta_title String SEO meta title (falls back to event.title) Join us for the Gala
event.meta_description String SEO meta description (falls back to a plain-text excerpt of the description) Our biggest night of the year.
event.og_title String Open Graph title (falls back to meta_title)
event.og_description String Open Graph description (falls back to meta_description)
event.og_image_url String Open Graph image URL (falls back to cover image)
event.twitter_title String Twitter/X title (falls back to og_title)
event.twitter_description String Twitter/X description (falls back to og_description)
event.twitter_share_text String Pre-filled text for Twitter/X share buttons Come to our gala!
event.email_share_subject String Pre-filled subject for email share buttons You're invited
event.email_share_body String Pre-filled body for email share buttons I thought you might like this event.
event.url String Path to the event page /events/gala
event.spots_remaining Integer Remaining capacity (blank when uncapped) 102
event.rsvp_open Boolean Whether registration is currently open true
event.rsvp_enabled Boolean Whether this event takes RSVPs at all (false = information-only listing) true
event.hosts String Free-text host line the organiser typed (falls back to the organisation name). A display string only — to link to the branch, use event.branch Brisbane Branch
event.branch Branch The branch running this event, in the same shape as page.branches[] — .name, .url, .state, .contact_email, .about, .committee, .member_count. Blank (never half-populated) for party-wide events and events run above branch level, so {% if event.branch %} is a safe test. As on a directory page, .url is blank when no published directory page lists the branch — guard the link on it, not on the branch {% if event.branch.url %}<a href="{{ event.branch.url }}">{{ event.branch.name }}</a>{% endif %}
event.has_branch Boolean Whether this event belongs to a branch true
event.capacity_label String Capacity state label (blank when uncapped) 102 spots left
event.published Boolean Whether the event is published (public) true
event.status String Event status (draft / published / cancelled) published
event.in_progress Boolean Event has started but not finished (alias: event.is_happening_now) true
event.upcoming Boolean Event hasn't started yet false
event.has_ended Boolean Event has finished — based on the end time, so multi-day events stay current while they run false
event.timing_label String Where the event sits in time (Upcoming / In progress / Past) In progress
event.starts_at Time Start time in the event's timezone — apply your own {{ … | date: "%-d %b" }} filter (used on directory/branch event cards)
event.ends_at Time End time in the event's timezone (blank if none)
event.time_zone String IANA timezone the event's times are shown in Australia/Brisbane
event.is_online Boolean Whether the event is online (alias of event.online) false
event.suburb String Suburb Yeronga
event.state String State QLD
event.thumbnail_url String Card thumbnail URL (resized; falls back to cover; blank when none) /rails/active_storage/representations/…

Example — event page hero + registration

<h1>{{ event.title }}</h1>
<p>{{ event.date }} · {{ event.time_range }}</p>
<p>{{ event.location }}</p>

<div class="prose">{{ event.description }}</div>

{% event_rsvp_form %}

Custom Fields

Custom fields let you attach additional editable content to any page. They're configured in the dashboard at Dashboard → Settings → Custom Fields and their values are stored alongside built-in template variables in page.template_data.

Accessing custom fields: Custom field values are accessed the same way as built-in template variables — directly as {{ page.your_field_key }}. The field key is set when you create the field in the dashboard (lowercase letters, numbers, and underscores).

Field types

TypeDescriptionExample usage
text Single-line text input {{ page.campaign_slogan }}
textarea Multi-line plain text {{ page.intro_paragraph }}
rich_text HTML rich text (output with {{ var }} — Liquid renders it as HTML) {{ page.about_section }}
image An image. On a page, chosen from its media library or an external URL; on a collection item, uploaded straight onto the item. Outputs a URL string either way. {{ item.headshot }}
url URL string {{ page.cta_link }}
number Numeric value {{ page.target_amount }}
select One value from a predefined list of options {{ page.layout_style }}
boolean True or false (stored as "1"/"" — check with {% if page.show_banner == '1' %}) {% if page.show_banner == '1' %}…{% endif %}
repeater Repeating group of sub-fields — value is an array of hashes. Iterate with {% for %}. {% for item in page.team_members %}{{ item.name }}{% endfor %}
date A date, stored as YYYY-MM-DD {{ item.elected_on }}
email Email address {{ item.contact_email }}
file A document — a PDF, a how-to-vote card. Uploaded onto a collection item, or an external URL. Outputs a URL either way, ready for an href. {{ item.htv_card }}
multi_select Several values from a predefined list — value is an array. Iterate with {% for %}. {% for issue in item.issues %}{{ issue }}{% endfor %}
reference Points at a live record — a branch, event, person, or an item in another collection. Outputs that record, so you can walk into its fields. {{ item.branch.name }}

Scoping

Field groups can be scoped to control which pages they appear on. More specific rules take priority.

ScopeWhere it applies
Specific page Only one named page — highest priority
Content template All pages using a particular content template
Page type All pages of a given type (petition, volunteer, etc.)
All pages Every page in the organisation — lowest priority

Example — text, rich_text, boolean

{{ page.campaign_slogan }}

{{ page.about_section }}

{% if page.show_banner == '1' %}
  <div class="banner">{{ page.banner_text }}</div>
{% endif %}

Example — repeater field

{% for member in page.team_members %}
  <div class="team-card">
    <img src="{{ member.photo }}" alt="{{ member.name }}">
    <h3>{{ member.name }}</h3>
    <p>{{ member.role }}</p>
  </div>
{% endfor %}

Donation Widgets

The three-step donation flow is rendered by {% donation_three_step_form %} and four theme-scoped step templates you can edit in Theme Editor.

Full donation page: add {% donation_three_step_form %} to your donation page layout (e.g. the donation_three_step content template).

Embed on another page: {% donation_three_step_form "your-donate-slug" %} — looks up the published donation page and uses it for amounts, copy, and Stripe settings.

Editing steps: Theme Editor → Widget Partials → donation_step_one, donation_step_two, donation_step_three, donation_step_thanks. Do not invoke these via {% widget %}.

Turbo frame contract: the tag and controller wrap each step in <turbo-frame id="donation_flow">. Step forms must post with data-turbo-frame="donation_flow".

Step 2 required fields: first_name, last_name, email, phone, and residential address via {% form_address prefix: residential, required: true %}.

Step 3 Stripe hooks: keep data-controller="donation-payment", #payment-element-3step, and #payment-errors-3step in donation_step_three.

Complex Components

Tags render complex components — forms, payment elements, sharing, and page embeds. Parameters shown in italics are optional.

snippet {% snippet 'key' %}

Inserts a reusable HTML snippet from the Theme Editor. The key is the snippet's template_key.

'key' — Quoted template key of the snippet to render
{% snippet 'footer_cta' %}
widget {% widget 'slug' with 'partial_name' %}

Embeds another published page's form inline. The slug can be a quoted string or a Liquid variable path. The partial_name refers to a widget_partial or snippet in the Theme Editor. Inside the partial, use widget_page for the embedded page's data. On a collection item template, add attribute_to: item to tag every submission with that item, so enquiries and donations through it are attributed to it in the CRM.

'slug' — Page slug — quoted string or variable path (e.g. page.custom_fields.donation_slug) 'partial_name' — Name of the widget partial or snippet to render attribute_to: — Optional. A collection item — usually `item` — to attribute the submission to.
{% widget 'contact' with 'contact_partial', attribute_to: item %}
collection_items {% collection_items 'handle', where: 'field', limit: 4 %}…{% endcollection_items %}

Renders a subset of a collection's items — a homepage rail, a featured strip. The matching items are assigned to collection_items inside the block, so the card markup stays in your template.

'handle' — The collection's handle, e.g. 'candidates' where: — Optional. Only items whose named field is set (or equals the value in equals:) equals: — Optional. The value where: must match limit: — Optional. Maximum number of items
{% collection_items 'candidates', where: 'featured', limit: 4 %}{% for item in collection_items %}{{ item.title }}{% endfor %}{% endcollection_items %}
hero_image {% hero_image %}

Renders a hero section background using the page's attached image. Falls back to a gradient if no image is attached. Params: fallback_gradient (CSS gradient string), overlay_class (Tailwind class for image overlay).

fallback_gradient — CSS gradient for when no image is attached. Default: navy gradient. overlay_class — Tailwind class for the dark overlay. Default: bg-black/30
{% hero_image fallback_gradient: 'linear-gradient(135deg, #1E3A5F, #2D6A4F)' overlay_class: 'bg-black/40' %}
video_player {% video_player %} or {% video_player "key" %}

Renders a Plyr video player for one of the page's media-library videos. With no argument it uses the item keyed "video", else the first uploaded video. Pass a key to play a specific video (useful when a page has several). Renders nothing if no matching video exists.

key — Optional media key of the video to play. Bare quoted ("intro") or key: "intro". Defaults to the "video" item or first video.
{% video_player "intro" %}
share_buttons {% share_buttons %}

Renders Twitter/X, Facebook, and email share buttons using the page's share text fields. Params: layout (vertical or horizontal), text (introductory label above buttons).

layout — "vertical" (default) or "horizontal" text — Label above the share buttons
{% share_buttons layout: 'horizontal' text: 'Spread the word:' %}
form_address {% form_address %}

Renders a full Australian address block (suburb, postcode, state) with AddressFinder autocomplete and a manual entry fallback. Use inside {{ page.form.open }} … {{ page.form.close }}.

prefix — "residential" (default) or "mailing" required — true (default) or false manual_entry — true or false — show manual entry link fields — Omit for the full address block, or "postcode" for a postcode-only field
{% form_address prefix: 'residential' required: true %}
form_interests {% form_interests %}

Renders a checkbox grid of interest/volunteer options read from the page's template data. Use inside {{ page.form.open }} … {{ page.form.close }}.

name — Field name. Default: "volunteer_interests" columns — Grid columns. Default: 2
{% form_interests name: 'volunteer_interests' columns: 2 %}
issues_form {% issues_form %}

Renders an issues/preferences checkbox grid (for contact pages) with a submit button.

columns — Grid columns. Default: 2
{% issues_form columns: 3 %}
petition_signatures {% petition_signatures %}

Renders a styled signature count block (online signatures + initial_signatures template variable). Includes a progress bar if petition_goal is set.

{% petition_signatures %}
recent_signers {% recent_signers %}

Renders a row of up to 5 recent petition signer badges (name + suburb). For full control, iterate page.recent_signers directly.

{% recent_signers %}
recent_donations_stats {% recent_donations_stats %}

Renders a stats card showing total donors and average donation amount for the organisation. Add per-page baseline numbers via the donation page's Initial Unique Contributors / Initial Average Amount settings — they are added on top of live donations.

{% recent_donations_stats %}
membership_type_cards {% membership_type_cards %}

Renders the membership tier selection UI with toggle between recurring and one-time plans. Reads from organization.paid_membership_types.

{% membership_type_cards %}
donation_three_step_form {% donation_three_step_form %}

Renders Step 1 of the three-step donation flow (amount and frequency selection) from the theme's donation_step_one template. Also injects the required Stripe.js script tag. On non-donation pages, pass the donation page slug: {% donation_three_step_form "donate-slug" %}. Edit individual steps in Theme Editor (donation_step_one/two/three/thanks).

slug — Optional. Published donation page slug when embedding on another page.
{% donation_three_step_form %}
{% donation_three_step_form "donate" %}
event_rsvp_form {% event_rsvp_form %}

Event page only: renders the registration / ticket selection / Stripe payment panel for the current event (via the event-rsvp controller). Injects Stripe.js when the event has paid tickets. Renders nothing when the event has RSVPs turned off (information-only listing). Use it in the `event` template alongside {{ event.* }} fields.

{% event_rsvp_form %}
stripe_amount_selector {% stripe_amount_selector %}

Renders donation amount buttons with a custom amount option. Use inside a Stripe-enabled donation form.

{% stripe_amount_selector %}
stripe_card_element {% stripe_card_element %}

Renders the Stripe.js card input container. Params: id (element ID), error_id (error container ID).

id — ID of the card element div. Default: "card-element" error_id — ID of the error display div. Default: "card-errors"
{% stripe_card_element id: 'card-element' error_id: 'card-errors' %}
quick_links {% quick_links %}

Renders a grid of up to 6 published pages as linked cards. Pages are ordered by their position in the dashboard.

{% quick_links %}
find_page {% find_page 'page-slug' %}

Looks up a published page by slug and sets a context variable found_page_SLUG to the page URL. The variable is nil if the page does not exist or is unpublished. Useful for building dynamic links. Takes the slug on its own — there is no slug: label.

slug — Quoted page slug, passed on its own (not as slug: '...')
{% find_page 'donate' %}
{% if found_page_donate %}
  <a href="{{ found_page_donate }}">Donate</a>
{% endif %}
cobber_credit {% cobber_credit %}

Renders a small "Powered by Cobber" attribution link. Typically placed in the footer.

{% cobber_credit %}

Form Filters

Pipe a rendered field through these filters to customise HTML attributes inline. Multiple filters can be chained: {{ page.form.email.input | add_class: "..." | placeholder: "..." | required }}

FilterSyntaxDescriptionExample
add_class | add_class: "classes" Merges one or more CSS classes into the element's class attribute. {{ page.form.email.input | add_class: "border rounded px-3 py-2" }}
placeholder | placeholder: "text" Sets the placeholder attribute on an input. {{ page.form.email.input | placeholder: "you@example.com" }}
attr | attr: "name", "value" Adds any attribute to the element. {{ page.form.email.input | attr: "data-controller", "form-field" }}
data | data: "name", "value" Adds a data-NAME attribute to the element (shorthand for attr with data- prefix). {{ page.form.email.input | data: "required-message", "Email is required" }}
id | id: "value" Sets the id attribute. {{ page.form.email.input | id: "email-field" }}
name | name: "value" Overrides the name attribute. {{ page.form.email.input | name: "custom_email" }}
value | value: "value" Sets the value attribute. {{ page.form.first_name.input | value: "" }}
required | required Adds the required attribute. {{ page.form.phone.input | required }}
disabled | disabled Adds the disabled attribute. {{ page.form.email.input | disabled }}
rows | rows: N Sets the rows attribute (textarea only). {{ page.form.message.input | rows: 6 }}
autocomplete | autocomplete: "value" Sets the autocomplete attribute (e.g. on, off, email, given-name). {{ page.form.email.input | autocomplete: "email" }}
where_group | where_group: "Group name" Filters page.person_custom_fields to a single custom field group. {% for field in page.person_custom_fields | where_group: "Donor compliance" %}

Standard Liquid

Cobber uses Liquid 5 — all standard tags and filters work. Below is a quick reference; see the Liquid docs for full detail.

Tags

TagDescriptionExample
if / elsif / else / endif Conditional blocks {% if page.title == 'Home' %}…{% endif %}
unless / endunless Inverse conditional (runs if NOT true) {% unless has_submitted == 'true' %}…{% endunless %}
case / when Multi-branch conditional {% case page.template_type %}{% when 'petition' %}…{% endcase %}
for / endfor Loop over an array. Supports limit:, offset:, reversed. {% for signer in page.recent_signers limit: 5 %}…{% endfor %}
assign Create or overwrite a variable {% assign goal = page.custom_fields.petition_goal | plus: 0 %}
capture / endcapture Capture rendered output into a variable {% capture share_url %}https://example.com/{{ page.slug }}{% endcapture %}
comment / endcomment Hidden notes — not rendered to the page {% comment %}TODO: update hero{% endcomment %}
raw / endraw Prevent Liquid from parsing enclosed content {% raw %}{{ this will not be parsed }}{% endraw %}
cycle Alternate values across loop iterations {% cycle 'odd', 'even' %}
break / continue Exit or skip to next iteration in a for loop {% if signer.suburb == '' %}{% continue %}{% endif %}

Filters

FilterDescriptionExample
upcase / downcase Change string case {{ organization.name | upcase }}
capitalize Capitalise first letter {{ page.slug | capitalize }}
truncate: N Truncate string to N characters {{ page.meta_description | truncate: 120 }}
truncatewords: N Truncate to N words {{ page.content | truncatewords: 30 }}
strip_html Remove all HTML tags {{ page.content | strip_html }}
replace: 'a', 'b' Replace all occurrences of a with b {{ page.title | replace: 'QLD', 'Queensland' }}
prepend / append Add text to start or end of a string {{ page.slug | prepend: '/' }}
split: 'delimiter' Split string into array {{ page.custom_fields.donation_amounts | split: ',' }}
join: 'separator' Join array elements into a string {{ tags | join: ', ' }}
size Length of string or array {{ page.recent_signers | size }}
first / last First or last element of an array {{ page.recent_signers | first }}
map: 'field' Extract a field from each array item {{ page.recent_signers | map: 'suburb' | join: ', ' }}
uniq Remove duplicate values from an array {{ suburbs | uniq }}
sort Sort an array {{ tags | sort }}
plus / minus / times / divided_by / modulo Arithmetic {{ page.signatures | divided_by: page.custom_fields.petition_goal | times: 100 }}
round / ceil / floor Round a number {{ organization.total_raised | round: 2 }}
date: 'format' Format a date string using strftime syntax {{ page.created_at | date: '%d/%m/%Y' }}
default: 'value' Fall back to a value if variable is nil/empty/false {{ page.meta_title | default: page.title }}
url_encode URL-encode a string (for query parameters) {{ page.url | url_encode }}
mailto_encode Encode text for mailto: subject/body (spaces as %20) {{ page.email_share_subject | mailto_encode }}
escape HTML-escape a string {{ page.title | escape }}

Email Template Variables

These variables are available in email campaign templates in addition to organization and current_year. They are not available in public page templates.

Person, Membership & Payment

Variable Type Description
person.first_name String Recipient's first name
person.last_name String Recipient's last name
person.display_name String Recipient's display name
person.email String Recipient's email addresses (comma-separated)
person.emails Array Recipient's email addresses
person.phone String Recipient's phone (mobile, home, or work)
person.suburb String Recipient's suburb
person.state String Recipient's state
person.federal_electorate String Recipient's federal electorate
person.state_electorate String Recipient's state electorate
person.volunteer_status String Volunteer status
person.volunteer? Boolean True if person is a volunteer
person.membership_status String Membership status (active, expired, etc.)
person.has_active_membership? Boolean True if person has an active membership
membership.number String Membership number
membership.status String Status (active, expired, pending, cancelled)
membership.type_name String Membership tier name
membership.type_price String Formatted price (e.g. $50/year)
membership.started_at String Start date (DD Month YYYY)
membership.expires_at String Expiry date (DD Month YYYY), or "Life member"
membership.days_until_expiry Integer Days until expiry
membership.display_name String Full membership display name (includes secondary person if couple)
payment.amount String Formatted payment amount (e.g. $50.00)
payment.amount_raw Decimal Raw numeric amount
payment.status String Payment status
payment.payment_type String Type of payment (donation, membership_fee, etc.)
payment.processed_at String Date payment was processed (DD Month YYYY)