Console
Examples

Testimonials

A simple example showing how to create and display customer testimonials with ratings, photos, and review dates.

Field types demonstrated

  • text - Customer names and companies
  • textarea - Testimonial text
  • number - Star ratings (1-5)
  • file (single) - Customer photos
  • date - Review dates

Collection definition

{
  name: "Testimonials",
  fields: [
    {
      type: "text",
      label: "Customer Name",
      name: "name",
      required: true
    },
    {
      type: "text",
      label: "Company",
      name: "company",
      required: false
    },
    {
      type: "text",
      label: "Job Title",
      name: "title",
      required: false
    },
    {
      type: "textarea",
      label: "Testimonial",
      name: "testimonial",
      required: true,
      help_text: "The customer's feedback or review"
    },
    {
      type: "number",
      label: "Rating",
      name: "rating",
      required: true,
      minimum: 1,
      maximum: 5,
      step: 1,
      help_text: "Rating out of 5 stars"
    },
    {
      type: "file",
      label: "Customer Photo",
      name: "photo",
      required: false
    },
    {
      type: "date",
      label: "Review Date",
      name: "review_date",
      required: true
    }
  ]
}

Testimonials grid

Display all testimonials sorted by date.

{# Load the testimonials collection #}
{% set testimonials = cms.collection('testimonials') %}

<section class="testimonials">
  <h2>What Our Customers Say</h2>

  {# Sort by review date, newest first #}
  <div class="testimonials-grid">
    {% for testimonial in testimonials | sort((a, b) => b.review_date <=> a.review_date) %}
      <div class="testimonial-card">
        {# Star rating display #}
        <div class="stars">
          {% for i in 1..5 %}
            {% if i <= testimonial.rating %}⭐{% else %}☆{% endif %}
          {% endfor %}
        </div>

        <blockquote>
          "{{ testimonial.testimonial }}"
        </blockquote>

        <div class="customer-info">
          {% if testimonial.photo %}
            {# Single file: access directly #}
            <img src="{{ testimonial.photo }}" alt="{{ testimonial.name }}">
          {% endif %}

          <div>
            <p class="name">{{ testimonial.name }}</p>
            {% if testimonial.title and testimonial.company %}
              <p>{{ testimonial.title }} at {{ testimonial.company }}</p>
            {% elseif testimonial.company %}
              <p>{{ testimonial.company }}</p>
            {% endif %}
          </div>
        </div>

        {# Date formatting #}
        <p class="date">{{ testimonial.review_date | date('F Y') }}</p>
      </div>
    {% endfor %}
  </div>
</section>

Display only 5-star reviews.

{# Load the testimonials collection #}
{% set testimonials = cms.collection('testimonials') %}

<section class="featured-testimonials">
  <h2>5-Star Reviews</h2>

  {# Filter for 5-star reviews only #}
  {% set five_star = testimonials | filter(t => t.rating == 5) | sort((a, b) => b.review_date <=> a.review_date) %}

  <div class="testimonials-slider">
    {% for testimonial in five_star | slice(0, 3) %}
      <div class="testimonial-slide">
        <div class="stars">⭐⭐⭐⭐⭐</div>
        <blockquote>"{{ testimonial.testimonial }}"</blockquote>
        <p class="author">
          — {{ testimonial.name }}
          {% if testimonial.company %}, {{ testimonial.company }}{% endif %}
        </p>
      </div>
    {% endfor %}
  </div>
</section>

Testimonials with average rating

Calculate and display average rating.

{# Load the testimonials collection #}
{% set testimonials = cms.collection('testimonials') %}

<section class="testimonials-section">
  {# Calculate average rating #}
  {% set total_rating = 0 %}
  {% for testimonial in testimonials %}
    {% set total_rating = total_rating + testimonial.rating %}
  {% endfor %}
  {% set average = (total_rating / testimonials | length) | round(1) %}

  <div class="rating-summary">
    <h2>Customer Reviews</h2>
    <div class="average-rating">
      <span class="score">{{ average }}</span>
      <div class="stars">
        {% for i in 1..5 %}
          {% if i <= average %}⭐{% else %}☆{% endif %}
        {% endfor %}
      </div>
      <p>Based on {{ testimonials | length }} reviews</p>
    </div>
  </div>

  <div class="testimonials-list">
    {% for testimonial in testimonials | sort((a, b) => b.review_date <=> a.review_date) %}
      <div class="testimonial">
        <div class="header">
          {% if testimonial.photo %}
            <img src="{{ testimonial.photo }}" alt="{{ testimonial.name }}">
          {% endif %}
          <div>
            <strong>{{ testimonial.name }}</strong>
            {% if testimonial.company %}
              <span>{{ testimonial.company }}</span>
            {% endif %}
            <div class="stars">
              {% for i in 1..testimonial.rating %}⭐{% endfor %}
            </div>
          </div>
        </div>
        <p>{{ testimonial.testimonial }}</p>
        <time>{{ testimonial.review_date | date('F j, Y') }}</time>
      </div>
    {% endfor %}
  </div>
</section>

Filter by rating

Let users filter testimonials by star rating.

{# Load the testimonials collection #}
{% set testimonials = cms.collection('testimonials') %}

<section class="testimonials-filtered">
  <h2>Customer Reviews</h2>

  {# Rating filter buttons (would need JavaScript to make interactive) #}
  <div class="rating-filters">
    <button data-rating="all">All Reviews</button>
    {% for rating in 5..1 %}
      <button data-rating="{{ rating }}">{{ rating }}⭐</button>
    {% endfor %}
  </div>

  {# Display all testimonials grouped by rating #}
  {% for rating in 5..1 %}
    {% set rated = testimonials | filter(t => t.rating == rating) %}
    {% if rated is not empty %}
      <div class="rating-group" data-rating="{{ rating }}">
        <h3>{{ rating }}-Star Reviews ({{ rated | length }})</h3>

        <div class="testimonials-list">
          {% for testimonial in rated | sort((a, b) => b.review_date <=> a.review_date) %}
            <div class="testimonial">
              <div class="stars">
                {% for i in 1..testimonial.rating %}⭐{% endfor %}
              </div>
              <p>"{{ testimonial.testimonial }}"</p>
              <p class="author">
                — {{ testimonial.name }}
                {% if testimonial.company %}, {{ testimonial.company }}{% endif %}
              </p>
            </div>
          {% endfor %}
        </div>
      </div>
    {% endif %}
  {% endfor %}
</section>

Recent reviews component

A simple sidebar component showing latest reviews.

{# Load the testimonials collection #}
{% set testimonials = cms.collection('testimonials') %}

<aside class="recent-reviews-widget">
  <h3>Recent Reviews</h3>

  {# Show 3 most recent reviews #}
  {% set recent = testimonials | sort((a, b) => b.review_date <=> a.review_date) | slice(0, 3) %}

  {% for testimonial in recent %}
    <div class="review-snippet">
      <div class="stars">
        {% for i in 1..testimonial.rating %}⭐{% endfor %}
      </div>
      <p>"{{ testimonial.testimonial | slice(0, 100) }}..."</p>
      <small>— {{ testimonial.name }}</small>
    </div>
  {% endfor %}

  <a href="/reviews" class="view-all">View All Reviews →</a>
</aside>

Key points

Number fields for ratings

Use minimum and maximum to constrain ratings:

{
  type: "number",
  minimum: 1,
  maximum: 5,
  step: 1  // Whole numbers only
}

Displaying star ratings

Use loops to generate star displays:

{# Show filled stars based on rating #}
{% for i in 1..5 %}
  {% if i <= testimonial.rating %}⭐{% else %}☆{% endif %}
{% endfor %}

{# Show only filled stars #}
{% for i in 1..testimonial.rating %}⭐{% endfor %}

Calculating averages

Canvas doesn't have a built-in average filter, so calculate manually:

{% set total = 0 %}
{% for item in items %}
  {% set total = total + item.rating %}
{% endfor %}
{% set average = (total / items | length) | round(1) %}

Date formatting

Format dates for display:

{{ testimonial.review_date | date('F Y') }}        {# January 2026 #}
{{ testimonial.review_date | date('F j, Y') }}     {# January 21, 2026 #}
{{ testimonial.review_date | date('M d, Y') }}     {# Jan 21, 2026 #}

Truncating text

Use slice filter to shorten long testimonials:

{# Show first 100 characters #}
{{ testimonial.testimonial | slice(0, 100) }}...

{# Show first 50 words #}
{{ testimonial.testimonial | split(' ') | slice(0, 50) | join(' ') }}...

Conditional display

Show company info only when available:

{% if testimonial.title and testimonial.company %}
  <p>{{ testimonial.title }} at {{ testimonial.company }}</p>
{% elseif testimonial.company %}
  <p>{{ testimonial.company }}</p>
{% elseif testimonial.title %}
  <p>{{ testimonial.title }}</p>
{% endif %}

Sorting by date

Always sort dates to show most recent first:

{# Newest first #}
testimonials | sort((a, b) => b.review_date <=> a.review_date)

{# Oldest first #}
testimonials | sort((a, b) => a.review_date <=> b.review_date)

Last updated on

On this page