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
- Assets & CDN - Image optimization
- Channels - Efficient querying
- Global Variables - Performance variables
- Theme Config - Asset organization
Build blazing-fast Nimbu themes with smart optimization!