Events
A complete example showing how to create and display an event calendar with dates, times, locations, and event features.
Field types demonstrated
text- Event names and locationstextarea- Descriptionsdate-time- Start and end timesurl- Registration linksradio- Event type (single choice)checkbox- Event features (multiple choices)number- Max attendeesfile(single) - Event banner
Collection definition
{
"name": "Events",
"fields": [
{
"type": "text",
"label": "Event Name",
"name": "name",
"required": true
},
{
"type": "textarea",
"label": "Description",
"name": "description",
"required": true
},
{
"type": "date-time",
"label": "Start Date & Time",
"name": "start_datetime",
"required": true
},
{
"type": "date-time",
"label": "End Date & Time",
"name": "end_datetime",
"required": true
},
{
"type": "text",
"label": "Location",
"name": "location",
"required": true
},
{
"type": "url",
"label": "Registration URL",
"name": "registration_url",
"required": false,
"help_text": "Link to external registration page"
},
{
"type": "radio",
"label": "Event Type",
"name": "event_type",
"required": true,
"options": ["In-Person", "Virtual", "Hybrid"]
},
{
"type": "checkbox",
"label": "Event Features",
"name": "features",
"required": false,
"options": ["Recording Available", "Q&A Session", "Networking Time", "Certificate Provided"]
},
{
"type": "number",
"label": "Max Attendees",
"name": "max_attendees",
"required": false,
"minimum": 1,
"help_text": "Leave blank for unlimited"
},
{
"type": "file",
"label": "Event Banner",
"name": "banner",
"required": false,
"multiple": false // Returns string URL
}
]
}Upcoming events calendar
Display all upcoming events sorted by date.
{# Load the events collection #}
{% set events = cms.collection('events') %}
<div class="events-calendar">
<h1>Upcoming Events</h1>
{# IMPORTANT: Convert datetime strings to date objects for comparison #}
{# Filter using date() to convert both the field and 'now' to comparable dates #}
{% set upcoming = events | filter(e => date(e.start_datetime) >= date('now')) | sort((a, b) => a.start_datetime <=> b.start_datetime) %}
{% for event in upcoming %}
<article class="event-card">
{% if event.banner %}
{# Single file: direct access as string #}
<img src="{{ event.banner }}" alt="{{ event.name }}">
{% endif %}
<h2>{{ event.name }}</h2>
<p class="date">
📅 {{ event.start_datetime | date('F j, Y') }}
⏰ {{ event.start_datetime | date('g:ia') }} -
{{ event.end_datetime | date('g:ia') }}
</p>
<p class="location">
📍 {{ event.location }}
{# Check radio field value - use exact string #}
{% if event.event_type == 'Virtual' %}
<span class="badge">Virtual Event</span>
{% elseif event.event_type == 'Hybrid' %}
<span class="badge">Hybrid Event</span>
{% endif %}
</p>
{% if event.max_attendees %}
<p>👥 Limited to {{ event.max_attendees }} attendees</p>
{% endif %}
<p>{{ event.description }}</p>
{# Checkbox field returns array - check if items are in array #}
{% if event.features is not empty %}
<div class="features">
<h3>What's Included:</h3>
<ul>
{% if 'Recording Available' in event.features %}
<li>✓ Recording available after event</li>
{% endif %}
{% if 'Q&A Session' in event.features %}
<li>✓ Live Q&A session</li>
{% endif %}
{% if 'Networking Time' in event.features %}
<li>✓ Networking opportunities</li>
{% endif %}
{% if 'Certificate Provided' in event.features %}
<li>✓ Certificate of attendance</li>
{% endif %}
</ul>
</div>
{% endif %}
{% if event.registration_url %}
<a href="{{ event.registration_url }}" class="btn-register">Register Now →</a>
{% endif %}
</article>
{% endfor %}
</div>Events by month
Group events by month for better organization.
{# Load the events collection #}
{% set events = cms.collection('events') %}
<div class="events-by-month">
<h1>Upcoming Events</h1>
{# Get upcoming events - convert strings to dates for comparison #}
{% set upcoming = events | filter(e => date(e.start_datetime) >= date('now')) | sort((a, b) => a.start_datetime <=> b.start_datetime) %}
{# Group by month (format as "2026-01" for grouping) #}
{% for month, month_events in upcoming | group_by(e => e.start_datetime | date('Y-m')) %}
<section class="month-section">
{# Display month name from first event #}
<h2>{{ month_events | first.start_datetime | date('F Y') }}</h2>
<div class="events-list">
{% for event in month_events %}
<div class="event-item">
<div class="date-badge">
<span class="day">{{ event.start_datetime | date('d') }}</span>
<span class="month">{{ event.start_datetime | date('M') }}</span>
</div>
<div class="event-info">
<h3>{{ event.name }}</h3>
<p>{{ event.start_datetime | date('g:ia') }} - {{ event.location }}</p>
</div>
</div>
{% endfor %}
</div>
</section>
{% endfor %}
</div>Filter by event type
Show only specific types of events (e.g., virtual events).
{# Load the events collection #}
{% set events = cms.collection('events') %}
<div class="virtual-events">
<h1>Virtual Events</h1>
{# Filter for virtual events only - use date() for comparison #}
{% set virtual = events | filter(e => e.event_type == 'Virtual' and date(e.start_datetime) >= date('now')) | sort((a, b) => a.start_datetime <=> b.start_datetime) %}
{% for event in virtual %}
<article class="event-card">
<span class="badge">Virtual Event</span>
<h2>{{ event.name }}</h2>
<p>{{ event.start_datetime | date('l, F j \\a\\t g:ia') }}</p>
<p>{{ event.description }}</p>
{% if event.registration_url %}
<a href="{{ event.registration_url }}" class="btn">Join Online</a>
{% endif %}
</article>
{% endfor %}
{% if virtual is empty %}
<p>No upcoming virtual events at this time.</p>
{% endif %}
</div>Key points
File field types
Single file (multiple: false) returns a string URL:
{# Direct access - it's a string #}
{% if event.banner %}
<img src="{{ event.banner }}">
{% endif %}Multiple files (multiple: true) returns an array:
{# Must loop or access by index #}
{% for img in event.images %}
<img src="{{ img }}">
{% endfor %}
{# Or access first item #}
<img src="{{ event.images[0] }}">Date-Time formatting
Canvas uses canvas date format characters (not Liquid):
{{ event.start_datetime | date('F j, Y') }} {# January 21, 2026 #}
{{ event.start_datetime | date('g:ia') }} {# 2:30pm #}
{{ event.start_datetime | date('l, F j, Y') }} {# Wednesday, January 21, 2026 #}
{{ event.start_datetime | date('F jS \\a\\t g:ia') }} {# January 21st at 2:30pm #}Escape literal characters with backslashes: \\a\\t for "at"
Radio fields
Radio buttons store a single value. Use exact string matching:
{% if event.event_type == 'Virtual' %} {# Not 'virtual' - use exact string! #}Checkbox fields
Checkboxes return an array of selected values. Check with in:
{% if 'Recording Available' in event.features %}
{# This feature is selected #}
{% endif %}Filtering by date
IMPORTANT: Date and datetime fields are stored as strings. You must convert them to date objects using date() before comparison:
{# ✅ CORRECT - Convert strings to date objects #}
events | filter(e => date(e.start_datetime) >= date('now'))
{# ❌ INCORRECT - String comparison won't work correctly #}
events | filter(e => e.start_datetime >= 'now')
{# Get future events #}
events | filter(e => date(e.start_datetime) >= date('now'))
{# Get past events #}
events | filter(e => date(e.start_datetime) < date('now'))
{# Get events in a specific date range #}
events | filter(e => date(e.start_datetime) >= date('2026-01-01') and date(e.start_datetime) < date('2026-02-01'))
{# Get events happening today #}
{% set today = date('now') | date('Y-m-d') %}
events | filter(e => date(e.start_datetime) | date('Y-m-d') == today)
{# Get events this week #}
events | filter(e => date(e.start_datetime) >= date('now') and date(e.start_datetime) <= date('+7 days'))
{# Get events this month #}
{% set this_month = date('now') | date('Y-m') %}
events | filter(e => date(e.start_datetime) | date('Y-m') == this_month)Arrow functions in group_by
You can use arrow functions to transform values for grouping:
{# Group by year-month #}
events | group_by(e => e.start_datetime | date('Y-m'))
{# Group by year only #}
events | group_by(e => e.start_datetime | date('Y'))Handling empty results
Always provide feedback when filters return no results:
{% if virtual is empty %}
<p>No virtual events available.</p>
{% endif %}Last updated on