Nimbu Developer Docs
Using Filters

Assets & CDN Filters

Generate asset URLs, process images with CDN, and create HTML tags for resources

Nimbu provides powerful CDN integration for assets, with automatic image processing, optimization, and responsive image generation.

Asset URLs

asset_url

Generate URL for theme assets:

{{ 'style.css' | asset_url }}
<!-- Output: /assets/themes/theme-slug/style.css -->

<link rel="stylesheet" href="{{ 'main.css' | asset_url }}">
<script src="{{ 'app.js' | asset_url }}"></script>

theme_image_url

Get theme image URL:

{{ 'logo.png' | theme_image_url }}

<img src="{{ 'banner.jpg' | theme_image_url }}" alt="Banner">

global_asset_url

Access global site assets:

{{ 'shared-logo.png' | global_asset_url }}

<img src="{{ 'favicon.ico' | global_asset_url }}">

Image Processing with CDN

filter

Apply comprehensive image transformations:

{{ image.url | filter, width: '800px', height: '600px' }}

{{ product.image.url | filter, width: '400px', cropping: 'fill' }}

{{ photo.url | filter, width: '1200px', height: '800px', gravity: 'face', quality: 85 }}

Parameters:

ParameterDescriptionValues
widthTarget width'300px', '1200px'
heightTarget height'400px', '800px'
croppingCrop mode'fill', 'fit', 'limit', 'pad', 'scale'
gravityCrop focus'center', 'face', 'north', 'south', 'east', 'west'
effectVisual effect'grayscale', 'sepia', 'blur', 'sharpen'
qualityJPEG quality1-100 (default: 80)
formatOutput format'jpg', 'png', 'webp', 'avif'
alphaTransparency0-100

Cropping Modes:

  • fill - Resize and crop to exact dimensions
  • fit - Fit within dimensions, maintain aspect ratio
  • limit - Only downscale, never upscale
  • pad - Fit and add padding to match dimensions
  • scale - Resize to dimensions, ignore aspect ratio

Gravity Options:

  • center - Center of image
  • face - Detected faces
  • north, south, east, west - Directional
  • north_east, north_west, south_east, south_west - Corners

grayscale

Convert image to grayscale:

{{ image.url | grayscale }}

<img src="{{ product.image.url | grayscale }}" alt="B&W {{ product.name }}">

Image Tag Helpers

image_tag

Generate complete image tag:

{{ 'logo.png' | theme_image_url | image_tag }}
<!-- Output: <img src="/assets/.../logo.png" alt="Logo"> -->

{{ image.url | image_tag, alt: product.name, class: 'product-image' }}

{{ photo | image_tag, alt: "Team photo", width: 800, height: 600 }}

With Lazy Loading:

{{ image.url | image_tag, loading: 'lazy', class: 'lazy-image' }}

Responsive Images

Srcset Generation

Create responsive image sets:

{% assign image_400 = product.image.url | filter, width: '400px' %}
{% assign image_800 = product.image.url | filter, width: '800px' %}
{% assign image_1200 = product.image.url | filter, width: '1200px' %}

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

Picture Element

Art direction with different crops:

<picture>
  <source
    media="(max-width: 600px)"
    srcset="{{ image.url | filter, width: '600px', height: '400px', cropping: 'fill', gravity: 'face' }}">
  <source
    media="(max-width: 1200px)"
    srcset="{{ image.url | filter, width: '1200px', height: '600px', cropping: 'fill' }}">
  <img
    src="{{ image.url | filter, width: '1920px', height: '800px', cropping: 'fill' }}"
    alt="{{ image.title }}">
</picture>

Stylesheet and Script Tags

stylesheet_tag

Generate stylesheet link:

{{ 'theme.css' | asset_url | stylesheet_tag }}
<!-- Output: <link href="/assets/.../theme.css" rel="stylesheet"> -->

{{ 'custom.css' | asset_url | stylesheet_tag, media: 'print' }}

script_tag

Generate script tag:

{{ 'app.js' | asset_url | script_tag }}
<!-- Output: <script src="/assets/.../app.js"></script> -->

{{ 'analytics.js' | asset_url | script_tag, defer: true }}
{{ 'polyfill.js' | asset_url | script_tag, async: true }}

Practical Examples

Product Images with Variants

<div class="product-gallery">
  <!-- Main image -->
  <div class="main-image">
    {% if product.images.first %}
      {% assign main_image = product.images.first.url %}
      <img
        src="{{ main_image | filter, width: '800px', height: '800px', cropping: 'fill' }}"
        srcset="{{ main_image | filter, width: '400px', height: '400px', cropping: 'fill' }} 400w,
                {{ main_image | filter, width: '800px', height: '800px', cropping: 'fill' }} 800w,
                {{ main_image | filter, width: '1200px', height: '1200px', cropping: 'fill' }} 1200w"
        sizes="(max-width: 768px) 400px, 800px"
        alt="{{ product.name }}"
        loading="eager">
    {% endif %}
  </div>

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

Hero Banner with Optimizations

<section class="hero">
  {% if page.hero_image %}
    <picture>
      <!-- Mobile: Portrait crop -->
      <source
        media="(max-width: 768px)"
        srcset="{{ page.hero_image.url | filter, width: '768px', height: '1024px', cropping: 'fill', gravity: 'center', format: 'webp' }} 768w"
        type="image/webp">

      <!-- Tablet: Landscape -->
      <source
        media="(max-width: 1200px)"
        srcset="{{ page.hero_image.url | filter, width: '1200px', height: '600px', cropping: 'fill', format: 'webp' }} 1200w"
        type="image/webp">

      <!-- Desktop: Full width -->
      <source
        srcset="{{ page.hero_image.url | filter, width: '1920px', height: '800px', cropping: 'fill', format: 'webp', quality: 85 }} 1920w"
        type="image/webp">

      <!-- Fallback JPG -->
      <img
        src="{{ page.hero_image.url | filter, width: '1920px', height: '800px', cropping: 'fill', quality: 85 }}"
        alt="{{ page.hero_title }}"
        loading="eager">
    </picture>
  {% endif %}

  <div class="hero-content">
    <h1>{{ page.hero_title }}</h1>
    <p>{{ page.hero_subtitle }}</p>
  </div>
</section>

Team Member Cards

<div class="team-grid">
  {% for member in channels.team.all %}
    <div class="team-card">
      {% if member.photo %}
        <div class="photo">
          <img
            src="{{ member.photo.url | filter, width: '300px', height: '300px', cropping: 'fill', gravity: 'face' }}"
            alt="{{ member.name }}"
            loading="lazy">
        </div>
      {% else %}
        <div class="photo placeholder">
          <img src="{{ 'team-placeholder.png' | theme_image_url }}" alt="{{ member.name }}">
        </div>
      {% endif %}

      <h3>{{ member.name }}</h3>
      <p class="role">{{ member.role }}</p>
    </div>
  {% endfor %}
</div>

Background Images

<section
  class="cta-section"
  style="background-image: url('{{ page.background.url | filter, width: '1920px', quality: 75, effect: 'blur' }}')">

  <div class="cta-content">
    <h2>{{ page.cta_title }}</h2>
    <a href="{{ page.cta_link }}" class="btn">{{ page.cta_text }}</a>
  </div>
</section>

Asset Preloading

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

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

  <!-- Stylesheets -->
  {{ 'theme.css' | asset_url | stylesheet_tag }}

  <!-- Critical JS -->
  {{ 'critical.js' | asset_url | script_tag }}
</head>
<div class="image-gallery">
  {% for image in page.gallery %}
    <a
      href="{{ image.url | filter, width: '1920px', quality: 90 }}"
      class="gallery-item"
      data-lightbox="gallery">

      <img
        src="{{ image.url | filter, width: '400px', height: '300px', cropping: 'fill' }}"
        alt="{{ image.title }}"
        loading="lazy">
    </a>
  {% endfor %}
</div>

Performance Best Practices

1. Use Appropriate Image Formats

<!-- ✅ Good: Modern formats with fallback -->
<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="...">
</picture>

<!-- ⚠️ Acceptable for simple cases -->
<img src="{{ image.url | filter, width: '800px' }}" alt="...">

2. Optimize Quality Settings

<!-- ✅ Good: Optimized quality -->
{{ image.url | filter, width: '1200px', quality: 80 }}

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

3. Lazy Load Below the Fold

<!-- Above the fold: Eager loading -->
<img src="{{ hero.url | filter, width: '1920px' }}" loading="eager" alt="Hero">

<!-- Below the fold: Lazy loading -->
<img src="{{ image.url | filter, width: '800px' }}" loading="lazy" alt="...">

4. Use Face Detection for People Photos

<!-- ✅ Good: Face-aware cropping -->
{{ member.photo.url | filter, width: '300px', height: '300px', cropping: 'fill', gravity: 'face' }}

<!-- ❌ Bad: Might crop out faces -->
{{ member.photo.url | filter, width: '300px', height: '300px', cropping: 'fill' }}

Next Steps

Optimize and deliver assets effectively with Nimbu's CDN filters!

On this page