Skip to content

Templates

Templates define the content and structure of specific page types in your Nimbu theme. While layouts provide the global HTML structure, templates focus on the unique content for each page.

How Templates Work

When a request comes to your Nimbu site:

  1. Nimbu matches the URL to a route
  2. Selects the appropriate template
  3. Renders the template inside the layout
  4. Returns the complete HTML

Template Types

Nimbu uses these standard template types:

TemplateRoutePurpose
index.liquid/Homepage
page.liquid/about, /contactGeneric content pages
blog.liquid/blogBlog listing page
article.liquid/blog/post-titleIndividual blog posts
product.liquid/products/product-slugProduct details
collection.liquid/collections/collection-slugProduct collections
customers/login.liquid/customers/loginCustomer login
customers/register.liquid/customers/registerCustomer registration
customers/account.liquid/customers/accountCustomer dashboard
404.liquidAny 404 errorNot found page

Template-Specific Variables

Each template type has access to specific variables:

Product Template

liquid
{{ product.name }}
{{ product.price }}
{{ product.description }}
{{ product.images }}
{{ product.variants }}
{{ product.on_sale }}

Page Template

liquid
{{ page.title }}
{{ page.content }}
{{ page.created_at }}
{{ page.translations }}

Article Template

liquid
{{ article.title }}
{{ article.content }}
{{ article.author }}
{{ article.published_at }}
{{ article.blog }}

Blog Template

liquid
{{ blog.name }}
{{ blog.articles }}
{{ blog.tags }}

See Global Variables for complete variable reference.

Creating Templates

Homepage (templates/index.liquid)

liquid
<section class="hero">
  <div class="container">
    <h1>{% editable_field hero_title %}Welcome to {{ site.name }}{% endeditable_field %}</h1>

    <div class="hero-content">
      {% editable_text hero_text %}
        <p>Build amazing websites with Nimbu.</p>
      {% endeditable_text %}
    </div>

    <a href="/shop" class="cta-button">
      {% translate 'cta.shop_now', default: 'Shop Now' %}
    </a>
  </div>
</section>

<section class="featured-products">
  <div class="container">
    <h2>Featured Products</h2>

    <div class="product-grid">
      {% for product in products.featured limit: 4 %}
        {% include 'product-card', product: product %}
      {% endfor %}
    </div>
  </div>
</section>

<section class="content-blocks">
  <div class="container">
    {% editable_canvas blocks %}
      {% repeatable "Text Block" %}
        {% include 'repeatables/text-block' %}
      {% endrepeatable %}

      {% repeatable "Image Gallery" %}
        {% include 'repeatables/gallery' %}
      {% endrepeatable %}
    {% endeditable_canvas %}
  </div>
</section>

Page Template (templates/page.liquid)

Generic template for content pages:

liquid
<article class="page">
  <header class="page-header">
    <div class="container">
      {% breadcrumbs %}
      <h1>{{ page.title }}</h1>

      {% if page.excerpt %}
        <p class="lead">{{ page.excerpt }}</p>
      {% endif %}
    </div>
  </header>

  <div class="page-content">
    <div class="container">
      {% editable_canvas content %}
        {% repeatable "Full Width Text" %}
          {% include 'repeatables/full-text' %}
        {% endrepeatable %}

        {% repeatable "Text & Image" %}
          {% include 'repeatables/text-image' %}
        {% endrepeatable %}

        {% repeatable "Cards" %}
          {% include 'repeatables/cards' %}
        {% endrepeatable %}

        {% repeatable "Call to Action" %}
          {% include 'repeatables/cta' %}
        {% endrepeatable %}
      {% endeditable_canvas %}
    </div>
  </div>
</article>

Product Template (templates/product.liquid)

Full product page with images, variants, and add to cart:

liquid
<div class="product-page">
  <div class="container">
    {% breadcrumbs %}

    <div class="product-main">
      <!-- Product Images -->
      <div class="product-images">
        {% if product.images.size > 0 %}
          <div class="main-image">
            <img src="{{ product.images.first.url | filter, width: '800px' }}"
                 alt="{{ product.name }}">
          </div>

          {% if product.images.size > 1 %}
            <div class="image-thumbnails">
              {% for image in product.images %}
                <img src="{{ image.url | filter, width: '100px' }}"
                     alt="{{ product.name }}"
                     data-full="{{ image.url | filter, width: '800px' }}">
              {% endfor %}
            </div>
          {% endif %}
        {% endif %}
      </div>

      <!-- Product Info -->
      <div class="product-info">
        <h1>{{ product.name }}</h1>

        <div class="product-price">
          {% if product.on_sale %}
            <span class="sale-price">{{ product.on_sale_price | money_with_currency }}</span>
            <span class="original-price">{{ product.price | money_with_currency }}</span>
          {% else %}
            <span class="price">{{ product.price | money_with_currency }}</span>
          {% endif %}
        </div>

        <div class="product-description">
          {{ product.description }}
        </div>

        <!-- Add to Cart Form -->
        {% if product.status != 'sold_out' %}
          {{ product | add_to_cart, btn_class: 'btn btn-primary', add_label: 'Add to Cart' }}
        {% else %}
          <p class="sold-out">This product is currently sold out.</p>
        {% endif %}

        <!-- Product Details -->
        <div class="product-details">
          <h3>Details</h3>
          <dl>
            <dt>SKU</dt>
            <dd>{{ product.sku }}</dd>

            {% if product.vendor %}
              <dt>Brand</dt>
              <dd>{{ product.vendor }}</dd>
            {% endif %}

            {% if product.type %}
              <dt>Type</dt>
              <dd>{{ product.type }}</dd>
            {% endif %}
          </dl>
        </div>
      </div>
    </div>

    <!-- Related Products -->
    <div class="related-products">
      <h2>You May Also Like</h2>

      <div class="product-grid">
        {% scope product_type_name == product.product_type_name %}
          {% for related_product in products.random limit: 4 %}
            {% unless related_product.id == product.id %}
              {% include 'product-card', product: related_product %}
            {% endunless %}
          {% endfor %}
        {% endscope %}
      </div>
    </div>
  </div>
</div>

Blog Template (templates/blog.liquid)

liquid
<div class="blog">
  <div class="container">
    <header class="blog-header">
      <h1>{{ blog.name }}</h1>
      {% if blog.description %}
        <p class="lead">{{ blog.description }}</p>
      {% endif %}
    </header>

    <!-- Article List -->
    <div class="articles">
      {% paginate blog.articles by 10 %}
        {% for article in blog.articles %}
          <article class="article-preview">
            {% if article.thumbnail.url %}
              <a href="{{ article.url }}" class="article-image">
                <img src="{{ article.thumbnail.url | filter, width: '400px' }}" alt="{{ article.title }}">
              </a>
            {% endif %}

            <div class="article-content">
              <h2><a href="{{ article.url }}">{{ article.title }}</a></h2>

              <p class="article-meta">
                {{ article.published_at | localized_date: 'long' }}
                {% if article.author %} · By {{ article.author }}{% endif %}
              </p>

              <p class="article-excerpt">{{ article.excerpt }}</p>

              <a href="{{ article.url }}" class="read-more">
                {% translate 'blog.read_more', default: 'Read More' %} →
              </a>
            </div>
          </article>
        {% endfor %}

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

    <!-- Sidebar -->
    <aside class="blog-sidebar">
      <!-- Tags -->
      {% if blog.tags.size > 0 %}
        <div class="widget">
          <h3>Topics</h3>
          <ul class="tag-list">
            {% for tag in blog.tags %}
              <li><a href="{{ blog.url }}?tag={{ tag | url_encode }}">{{ tag }}</a></li>
            {% endfor %}
          </ul>
        </div>
      {% endif %}

      <!-- Recent Articles -->
      <div class="widget">
        <h3>Recent Posts</h3>
        <ul class="recent-articles">
          {% for article in blog.articles.latest limit: 5 %}
            <li><a href="{{ article.url }}">{{ article.title }}</a></li>
          {% endfor %}
        </ul>
      </div>
    </aside>
  </div>
</div>

Article Template (templates/article.liquid)

liquid
<article class="article">
  <header class="article-header">
    <div class="container">
      <p class="article-category">
        <a href="{{ article.blog.url }}">{{ article.blog.name }}</a>
      </p>

      <h1>{{ article.title }}</h1>

      <p class="article-meta">
        {{ article.published_at | localized_date: 'long' }}
        {% if article.author %} by {{ article.author }}{% endif %}
      </p>
    </div>
  </header>

  {% if article.header_image.url %}
    <div class="article-featured-image">
      <img src="{{ article.header_image.url | filter, width: '1200px' }}" alt="{{ article.title }}">
    </div>
  {% endif %}

  <div class="article-content">
    <div class="container">
      {{ article.content }}
    </div>
  </div>

  <footer class="article-footer">
    <div class="container">
      <!-- Tags -->
      {% if article.tags.size > 0 %}
        <div class="article-tags">
          {% for tag in article.tags %}
            <a href="{{ article.blog.url }}?tag={{ tag | url_encode }}" class="tag">{{ tag }}</a>
          {% endfor %}
        </div>
      {% endif %}

      <!-- Previous/Next Navigation -->
      <nav class="article-navigation">
        {% if article.previous %}
          <a href="{{ article.previous.url }}" class="prev">
            ← {{ article.previous.title }}
          </a>
        {% endif %}

        {% if article.next %}
          <a href="{{ article.next.url }}" class="next">
            {{ article.next.title }} →
          </a>
        {% endif %}
      </nav>
    </div>
  </footer>
</article>

Customer Login (templates/customers/login.liquid)

liquid
{%- layout 'minimal' -%}

<div class="login-page">
  <div class="login-container">
    <h1>{% translate 'customer.login.title', default: 'Sign In' %}</h1>

    {% form customers.login, class: 'login-form' %}
      {% error_messages_for form_model %}

      {% input 'email', as: 'email', label: 'Email', required: true, autofocus: true %}
      {% input 'password', as: 'password', label: 'Password', required: true %}

      <div class="form-actions">
        {% submit_tag 'Sign In', class: 'btn btn-primary' %}
      </div>

      <div class="form-links">
        <a href="/customers/forgot_password">
          {% translate 'customer.login.forgot_password', default: 'Forgot password?' %}
        </a>
        <a href="/customers/register">
          {% translate 'customer.login.create_account', default: 'Create account' %}
        </a>
      </div>
    {% endform %}
  </div>
</div>

Template Routing

Nimbu automatically routes URLs to templates:

URL PatternTemplateVariables
/index.liquidsite
/aboutpage.liquidpage
/products/shoesproduct.liquidproduct
/collections/summercollection.liquidcollection
/blogblog.liquidblog
/blog/hello-worldarticle.liquidarticle, blog

Custom Templates

Create custom templates for specific pages by matching the page slug:

templates/contact.liquid for /contact:

liquid
<div class="contact-page">
  <div class="container">
    <h1>{{ page.title }}</h1>

    <div class="contact-grid">
      <div class="contact-info">
        {% editable_text contact_info %}
          <p>Get in touch with us!</p>
        {% endeditable_text %}
      </div>

      <div class="contact-form">
        {% form channels.contact, class: 'form' %}
          {% input 'name', label: 'Name', required: true %}
          {% input 'email', as: 'email', label: 'Email', required: true %}
          {% text_area 'message', label: 'Message', rows: 5, required: true %}
          {% submit_tag 'Send Message', class: 'btn btn-primary' %}
        {% endform %}
      </div>
    </div>
  </div>
</div>

Template Best Practices

1. Keep Templates Focused

Each template should handle one page type. Don't try to make a template do too much.

2. Use Snippets for Shared Components

Extract repeated elements into snippets:

liquid
<!-- In product.liquid -->
{% include 'product-images', product: product %}
{% include 'product-info', product: product %}
{% include 'related-products', product: product %}

3. Make Content Editable

Use editable regions so clients can update content:

liquid
{% editable_canvas content %}
  {% repeatable "Section" %}
    {% include 'section' %}
  {% endrepeatable %}
{% endeditable_canvas %}

4. Handle Empty States

Always check if data exists:

liquid
{% if products.size > 0 %}
  <!-- Show products -->
{% else %}
  <p>No products found.</p>
{% endif %}

5. Optimize Performance

Use caching for expensive operations:

liquid
{% cache 'product-grid', expires_in: 3600 %}
  {% for product in products.all %}
    {% include 'product-card', product: product %}
  {% endfor %}
{% endcache %}

Next Steps

Continue to Snippets to learn about creating reusable components.