CMS Grid Tag Reference
The {% cmsgrid %} tag provides a powerful way to display collections of content in customizable grid layouts with built-in styling and helper methods.
Syntax
{% cmsgrid objects from 'collection' with 'classes' as 'tag' %}
{# Template content with {{ object }} and {{ collection }} variables #}
{% endcmsgrid %}
Parameters
objects(required) - Array of objects to displayfrom 'collection'(optional) - Collection name, available as{{ collection }}variablewith 'classes'(optional) - CSS classes for the grid containeras 'tag'(optional) - HTML tag for grid items (default:div)
Basic Examples
Simple Blog Grid
{% cmsgrid cms.collection.objects('blog') from 'blog' with 'blog compact' %}
<h3>{{ object.title }}</h3>
<p>{{ object.summary }}</p>
<time>{{ cms.render.grid.date(object.date) }}</time>
{% endcmsgrid %}
Using Built-in Templates
{% cmsgrid cms.collection.objects('blog') from 'blog' with 'blog list' %}
{% include 'grid/blog.twig' %}
{% endcmsgrid %}
Custom Item Tags
{% cmsgrid cms.collection.objects('products') from 'products' with 'products grid' as 'article' %}
<h4>{{ object.name }}</h4>
<span class="price">{{ object.price|price }}</span>
{% endcmsgrid %}
Available Variables
{{ object }}
Each object in the collection array with all its properties:
{{ object.title }}
{{ object.summary }}
{{ object.date }}
{{ object.image }}
{{ object.tags }}
{{ collection }}
The collection name from the from parameter:
{% if collection == 'blog' %}
<div class="blog-specific-content">...</div>
{% endif %}
Grid Helper Methods
Access via cms.render.grid.* for formatted HTML output:
Date Formatting
{{ cms.render.grid.date(object.date, 'relative') }} {# "2 days ago" #}
{{ cms.render.grid.date(object.date, 'short') }} {# "Mar 15, 2024" #}
Text Excerpts
{{ cms.render.grid.excerpt(object.summary, 150) }} {# Truncated with HTML wrapper #}
Tag Lists
{{ cms.render.grid.tags(object.tags) }} {# Formatted tag list #}
{{ cms.render.grid.tags(object.tags, '/tag/') }} {# With links #}
Price Formatting
{{ cms.render.grid.price(object.price) }} {# $19.99 with HTML wrapper #}
Meta Information
{{ cms.render.grid.meta(object.author) }} {# Formatted meta data #}
Image Handling
Use cms.render.image() with the object and collection context:
{% if object.image %}
{{ cms.render.image(object, {w: 400}, {collection: collection, property: 'image'}) }}
{% endif %}
Built-in Templates
Blog Template (grid/blog.twig)
- Displays image, title, date, summary, and tags
- Optimized for blog posts and articles
Feed Template (grid/feed.twig)
- Shows date, title, content excerpt
- Designed for news feeds and updates
Generic Template (grid/generic.twig)
- Basic image, title, and summary layout
- Works with any collection type
CSS Grid Classes
The grid system includes comprehensive CSS with design system variables:
Layout Classes
compact- Smaller cards and gapswide- Larger cards and spacinglist- Single column layoutmasonry- Masonry-style layout
Spacing Classes
gap-sm,gap-md,gap-lg,gap-xl- Control grid gapspadding-sm,padding-md,padding-lg- Control card padding
Style Classes
no-border- Remove card bordersno-shadow- Remove shadows and hover effectsflat- Completely flat styling
Item Classes
cms-grid-item-full- Makes an item span the full width of the grid (all columns). Useful for featured items, section headers, or full-width content within a grid.
{% cmsgrid cms.collection.objects('blog') from 'blog' with 'blog' %}
{% if loop.first %}
{# Featured post spans full width #}
<div class="cms-grid-item cms-grid-item-full">
<h2>{{ object.title }}</h2>
<p>{{ object.summary }}</p>
</div>
{% else %}
<h3>{{ object.title }}</h3>
<p>{{ object.summary }}</p>
{% endif %}
{% endcmsgrid %}
Note: A .cms-load-more button placed as the last child inside a cms-grid will automatically span the full width and center itself.
CSS Variables
Override these variables on .cms-grid or a custom class to customize the grid appearance.
Grid Layout
| Variable | Default | Description |
|---|---|---|
--cms-grid-gap |
1rem |
Gap between grid items |
--cms-grid-min-column-width |
250px |
Minimum column width for auto-fill columns |
--cms-grid-columns |
repeat(auto-fill, minmax(..., 1fr)) |
Grid column template |
--cms-grid-align-items |
stretch |
Vertical alignment of grid items |
--cms-grid-justify-items |
stretch |
Horizontal alignment of grid items |
Card Styling
| Variable | Default | Description |
|---|---|---|
--cms-card-padding |
1rem |
Padding inside each card |
--cms-card-bg |
#ffffff |
Card background color |
--cms-card-border |
1px solid oklch(...) |
Card border |
--cms-card-border-radius |
var(--totalform-radius) |
Card corner radius |
--cms-card-shadow |
0 1px 3px oklch(0.2 0 0 / 0.1) |
Card box shadow |
--cms-card-hover-shadow |
0 4px 12px oklch(0.2 0 0 / 0.15) |
Card shadow on hover |
--cms-card-transition |
all 0.2s ease |
Card hover transition |
Typography
| Variable | Default | Description |
|---|---|---|
--cms-text-color |
oklch(var(--totalform-text-color)) |
Body text color |
--cms-heading-color |
oklch(var(--totalform-darkgray)) |
Heading color |
--cms-meta-color |
oklch(var(--totalform-lightgray)) |
Meta/secondary text color |
--cms-link-color |
oklch(var(--totalform-accent)) |
Link color |
Spacing & Images
| Variable | Default | Description |
|---|---|---|
--cms-item-spacing |
0.5rem |
Spacing between elements inside a card |
--cms-image-aspect-ratio |
16 / 9 |
Aspect ratio for featured images |
--cms-image-border-radius |
calc(var(--cms-card-border-radius) * 0.5) |
Image corner radius |
Example: Custom Grid Theme
.my-blog-grid {
--cms-grid-min-column-width: 300px;
--cms-grid-gap: 2rem;
--cms-card-bg: #f9f9f9;
--cms-card-border: none;
--cms-card-shadow: none;
--cms-card-hover-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
--cms-image-aspect-ratio: 3 / 2;
--cms-card-padding: 1.5rem;
}
{% cmsgrid cms.collection.objects('blog') from 'blog' with 'my-blog-grid' %}
{% include 'grid/blog.twig' %}
{% endcmsgrid %}
Real-World Examples
E-commerce Products
{% cmsgrid cms.collection.objects('products') from 'products' with 'products grid compact' %}
{% if object.image %}
{{ cms.render.image(object, {w: 300, h: 300}, {collection: collection, property: 'image'}) }}
{% endif %}
<h4>{{ object.name }}</h4>
<p class="price">{{ cms.render.grid.price(object.price) }}</p>
{% if object.sale_price %}
<p class="sale">{{ cms.render.grid.price(object.sale_price) }}</p>
{% endif %}
{% endcmsgrid %}
Team Members
{% cmsgrid cms.collection.objects('team') from 'team' with 'team grid' %}
{{ cms.render.image(object, {w: 200, h: 200}, {collection: collection, property: 'photo'}) }}
<h3>{{ object.name }}</h3>
<p class="role">{{ object.position }}</p>
<p>{{ cms.render.grid.excerpt(object.bio, 100) }}</p>
{% endcmsgrid %}
News Feed
{% cmsgrid cms.collection.objects('news') from 'feed' with 'feed list' %}
{% include 'grid/feed.twig' %}
{% endcmsgrid %}
Advanced Usage
Conditional Templates
{% cmsgrid cms.collection.objects('mixed') from collection_name %}
{% if collection == 'blog' %}
{% include 'grid/blog.twig' %}
{% elseif collection == 'products' %}
<h4>{{ object.name }}</h4>
<p>{{ cms.render.grid.price(object.price) }}</p>
{% else %}
{% include 'grid/generic.twig' %}
{% endif %}
{% endcmsgrid %}
Dynamic Collection Names
{% set collection_type = get.type|default('blog') %}
{% cmsgrid cms.collection.objects(collection_type) from collection_type with 'responsive-grid' %}
{# Template adapts based on collection type #}
{% endcmsgrid %}
Best Practices
- Use
fromparameter - Always specify collection for proper image handling - Leverage helper methods - Use
cms.render.grid.*for consistent formatting - Include built-in templates - Saves time and ensures consistency
- Combine CSS classes - Mix layout, spacing, and style classes as needed
- Handle empty states - Check for content before displaying
The {% cmsgrid %} tag provides a flexible, powerful system for displaying content grids while maintaining design consistency and providing helpful formatting utilities.