Skip to content

Performance Optimization

Nimbu provides powerful caching, pagination, and optimization tools to build lightning-fast websites.

Caching

{% cache %}

Cache template fragments for improved performance:

liquid
{% cache 'header-nav' %}
  {% nav main, class: "main-menu" %}
{% endcache %}

{% cache 'featured-products', expires_in: 3600 %}
  {% for product in products | where: "featured", true | limit: 4 %}
    {% include 'product-card', product: product %}
  {% endfor %}
{% endcache %}

Parameters:

  • First argument: Cache key (string or variable)
  • expires_in: Cache duration in seconds

Cache Keys

Use dynamic cache keys for user-specific content:

liquid
{% cache 'cart-summary-' | append: customer.id %}
  <!-- Customer-specific cart -->
{% endcache %}

{% assign cache_key = 'product-' | append: product.id | append: '-' | append: locale %}
{% cache cache_key, expires_in: 7200 %}
  <!-- Product details cached per locale -->
{% endcache %}

Cache Invalidation

Caches automatically invalidate when content updates. Manual cache busting with versioning:

liquid
{% cache 'homepage-v2' %}
  <!-- Update 'v2' to 'v3' to bust cache -->
{% endcache %}

Pagination

{% paginate %}

Paginate large collections:

liquid
{% paginate blog.articles by 20 %}
  {% for article in blog.articles %}
    <article>
      <h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
      <p>{{ article.excerpt }}</p>
    </article>
  {% endfor %}

  {{ paginate | default_pagination }}
{% endpaginate %}

Pagination Object:

liquid
{{ paginate.current_page }}     <!-- Current page number -->
{{ paginate.pages }}             <!-- Total pages -->
{{ paginate.items }}             <!-- Total items -->
{{ paginate.page_size }}         <!-- Items per page -->
{{ paginate.next.url }}          <!-- Next page URL -->
{{ paginate.previous.url }}      <!-- Previous page URL -->

Custom Pagination

liquid
{% paginate products by 24 %}
  <div class="products-grid">
    {% for product in products %}
      {% include 'product-card', product: product %}
    {% endfor %}
  </div>

  <nav class="pagination" aria-label="Pagination">
    {% if paginate.previous %}
      <a href="{{ paginate.previous.url }}" class="prev">Previous</a>
    {% endif %}

    <span class="page-info">
      Page {{ paginate.current_page }} of {{ paginate.pages }}
    </span>

    {% if paginate.next %}
      <a href="{{ paginate.next.url }}" class="next">Next</a>
    {% endif %}
  </nav>
{% endpaginate %}

Image Optimization

Responsive Images

liquid
{% assign img_400 = product.image.url | filter, width: '400px', quality: 80 %}
{% assign img_800 = product.image.url | filter, width: '800px', quality: 80 %}
{% assign img_1200 = product.image.url | filter, width: '1200px', quality: 80 %}

<img
  src="{{ img_800 }}"
  srcset="{{ img_400 }} 400w,
          {{ img_800 }} 800w,
          {{ img_1200 }} 1200w"
  sizes="(max-width: 600px) 400px,
         (max-width: 1200px) 800px,
         1200px"
  alt="{{ product.name }}"
  loading="lazy">

WebP with Fallback

liquid
<picture>
  <source
    srcset="{{ image.url | filter, width: '800px', format: 'webp' }}"
    type="image/webp">
  <source
    srcset="{{ image.url | filter, width: '800px', format: 'jpg' }}"
    type="image/jpeg">
  <img
    src="{{ image.url | filter, width: '800px' }}"
    alt="{{ image.title }}"
    loading="lazy">
</picture>

Lazy Loading

liquid
<!-- Native lazy loading -->
<img
  src="{{ product.image.url | filter, width: '600px' }}"
  alt="{{ product.name }}"
  loading="lazy">

<!-- Below-the-fold images -->
{% for image in gallery %}
  <img
    src="{{ image.url | filter, width: '400px' }}"
    alt="{{ image.title }}"
    loading="lazy">
{% endfor %}

Asset Optimization

Preload Critical Assets

liquid
<head>
  <!-- Preload hero image -->
  <link
    rel="preload"
    as="image"
    href="{{ page.hero_image.url | filter, width: '1920px', format: 'webp' }}">

  <!-- Preload critical fonts -->
  <link
    rel="preload"
    as="font"
    href="{{ 'Inter-Regular.woff2' | asset_url }}"
    type="font/woff2"
    crossorigin>

  <!-- Preload critical CSS -->
  <link rel="preload" href="{{ 'critical.css' | asset_url }}" as="style">
</head>

Defer Non-Critical Scripts

liquid
<!-- Critical JS -->
{{ 'critical.js' | asset_url | script_tag }}

<!-- Deferred JS -->
<script src="{{ 'analytics.js' | asset_url }}" defer></script>
<script src="{{ 'chat-widget.js' | asset_url }}" async></script>

CSS Optimization

liquid
<!-- Inline critical CSS -->
<style>
  /* Critical above-the-fold styles */
  .header { ... }
  .hero { ... }
</style>

<!-- Load full stylesheet -->
<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}" media="print" onload="this.media='all'">

Query Optimization

Limit Results

liquid
<!-- ✅ Good: Limit queries -->
{% assign recent = blog.articles | limit: 10 %}

<!-- ❌ Bad: Load everything -->
{% assign recent = blog.articles %}

Scope Before Sort

liquid
<!-- ✅ Good: Filter first, then sort -->
{% scope featured == true %}
{% sort price asc %}
  {% for product in products | limit: 12 %}
    <!-- Product display -->
  {% endfor %}
{% endsort %}
{% endscope %}

<!-- ❌ Bad: Sort then filter (wastes processing) -->
{% sort price asc %}
{% scope featured == true %}
  {% for product in products %}
    <!-- Product display -->
  {% endfor %}
{% endscope %}
{% endsort %}

Cache Expensive Queries

liquid
{% cache 'top-selling-products', expires_in: 3600 %}
  {% assign top_sellers = products | sort: "sales_count" | reverse | limit: 8 %}

  {% for product in top_sellers %}
    {% include 'product-card', product: product %}
  {% endfor %}
{% endcache %}

Practical Examples

Cached Homepage

liquid
<div class="homepage">
  <!-- Hero: Not cached (editable content) -->
  <section class="hero">
    {% editable_canvas hero %}
      <!-- Dynamic hero content -->
    {% endeditable_canvas %}
  </section>

  <!-- Featured products: Cached 1 hour -->
  <section class="featured-products">
    {% cache 'homepage-featured', expires_in: 3600 %}
      <h2>Featured Products</h2>
      <div class="product-grid">
        {% scope featured == true %}
          {% for product in products | limit: 8 %}
            {% include 'product-card', product: product %}
          {% endfor %}
        {% endscope %}
      </div>
    {% endcache %}
  </section>

  <!-- Latest articles: Cached 30 minutes -->
  <section class="latest-articles">
    {% cache 'homepage-articles', expires_in: 1800 %}
      <h2>Latest News</h2>
      {% for article in blog.articles | limit: 3 %}
        {% include 'article-card', article: article %}
      {% endfor %}
    {% endcache %}
  </section>
</div>

Paginated Product Catalog

liquid
{% paginate products by 24 %}
  <div class="catalog-header">
    <h1>All Products</h1>
    <p>Showing {{ paginate.current_offset | plus: 1 }}-{{ paginate.current_offset | plus: paginate.page_size | at_most: paginate.items }} of {{ paginate.items }} products</p>
  </div>

  <div class="products-grid">
    {% for product in products %}
      <div class="product-card">
        <a href="{{ product.url }}">
          <img
            src="{{ product.images.first.url | filter, width: '400px', quality: 80 }}"
            alt="{{ product.name }}"
            loading="lazy">
        </a>
        <h3>{{ product.name }}</h3>
        <p class="price">{{ product.price | money_with_currency }}</p>
      </div>
    {% endfor %}
  </div>

  <!-- SEO-friendly pagination -->
  <nav class="pagination" aria-label="Product pages">
    {% if paginate.previous %}
      <a href="{{ paginate.previous.url }}" rel="prev">← Previous</a>
    {% endif %}

    {% for page in paginate.parts %}
      {% if page.is_link %}
        <a href="{{ page.url }}">{{ page.title }}</a>
      {% else %}
        <span class="current">{{ page.title }}</span>
      {% endif %}
    {% endfor %}

    {% if paginate.next %}
      <a href="{{ paginate.next.url }}" rel="next">Next →</a>
    {% endif %}
  </nav>
{% endpaginate %}

Progressive Image Loading

liquid
<div class="product-gallery">
  <!-- Hero image: Eager loading -->
  <div class="main-image">
    <img
      src="{{ product.images.first.url | filter, width: '800px' }}"
      alt="{{ product.name }}"
      loading="eager">
  </div>

  <!-- Thumbnails: Lazy loading -->
  <div class="thumbnails">
    {% for image in product.images %}
      <button class="thumbnail">
        <img
          src="{{ image.url | filter, width: '100px' }}"
          alt="{{ product.name }} - Image {{ forloop.index }}"
          loading="lazy">
      </button>
    {% endfor %}
  </div>
</div>

Optimized Blog Archive

liquid
{% paginate blog.articles by 20 %}
  <div class="blog-archive">
    <h1>{{ blog.name }}</h1>

    {% cache 'blog-categories', expires_in: 3600 %}
      <!-- Cached category nav -->
      <nav class="categories">
        {% assign categories = blog.articles | map: "category" | uniq %}
        {% for category in categories %}
          <a href="/blog/category/{{ category | parameterize }}">
            {{ category }}
          </a>
        {% endfor %}
      </nav>
    {% endcache %}

    <!-- Article list -->
    <div class="articles">
      {% for article in blog.articles %}
        <article class="article-preview">
          {% if article.image %}
            <img
              src="{{ article.image.url | filter, width: '400px', quality: 75 }}"
              alt="{{ article.title }}"
              loading="lazy">
          {% endif %}

          <h2><a href="{{ article.url }}">{{ article.title }}</a></h2>

          <div class="meta">
            <time>{{ article.published_at | localized_date: "short" }}</time>
            <span class="author">{{ article.author.name }}</span>
          </div>

          <p>{{ article.excerpt | truncatewords: 50 }}</p>

          <a href="{{ article.url }}" class="read-more">Read More →</a>
        </article>
      {% endfor %}
    </div>

    {{ paginate | default_pagination }}
  </div>
{% endpaginate %}

Best Practices

1. Cache Strategically

liquid
<!-- ✅ Good: Cache static content -->
{% cache 'footer-links', expires_in: 86400 %}
  {% nav footer %}
{% endcache %}

<!-- ❌ Bad: Cache user-specific content -->
{% cache 'cart' %}
  {{ cart.items.size }} items
{% endcache %}

2. Optimize Images

liquid
<!-- ✅ Good: Appropriate quality and size -->
{{ image.url | filter, width: '800px', quality: 80, format: 'webp' }}

<!-- ❌ Bad: Unnecessary quality -->
{{ image.url | filter, width: '800px', quality: 100 }}

3. Limit Query Scope

liquid
<!-- ✅ Good: Limit early -->
{% assign featured = products | where: "featured", true | limit: 4 %}

<!-- ❌ Bad: Process all, then limit -->
{% assign all = products | where: "featured", true %}
{% for product in all | limit: 4 %}

4. Use Pagination for Large Sets

liquid
<!-- ✅ Good: Paginate large collections -->
{% paginate blog.articles by 20 %}

<!-- ❌ Bad: Load 1000s of items -->
{% for article in blog.articles %}

Next Steps

Build blazing-fast Nimbu themes with smart optimization!