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:
- Nimbu matches the URL to a route
- Selects the appropriate template
- Renders the template inside the layout
- Returns the complete HTML
Template Types
Nimbu uses these standard template types:
| Template | Route | Purpose |
|---|---|---|
index.liquid | / | Homepage |
page.liquid | /about, /contact | Generic content pages |
blog.liquid | /blog | Blog listing page |
article.liquid | /blog/post-title | Individual blog posts |
product.liquid | /products/product-slug | Product details |
collection.liquid | /collections/collection-slug | Product collections |
customers/login.liquid | /customers/login | Customer login |
customers/register.liquid | /customers/register | Customer registration |
customers/account.liquid | /customers/account | Customer dashboard |
404.liquid | Any 404 error | Not found page |
Template-Specific Variables
Each template type has access to specific variables:
Product Template
{{ product.name }}
{{ product.price }}
{{ product.description }}
{{ product.images }}
{{ product.variants }}
{{ product.on_sale }}Page Template
{{ page.title }}
{{ page.content }}
{{ page.created_at }}
{{ page.translations }}Article Template
{{ article.title }}
{{ article.content }}
{{ article.author }}
{{ article.published_at }}
{{ article.blog }}Blog Template
{{ blog.name }}
{{ blog.articles }}
{{ blog.tags }}See Global Variables for complete variable reference.
Creating Templates
Homepage (templates/index.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:
<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:
<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)
<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)
<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)
{%- 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 Pattern | Template | Variables |
|---|---|---|
/ | index.liquid | site |
/about | page.liquid | page |
/products/shoes | product.liquid | product |
/collections/summer | collection.liquid | collection |
/blog | blog.liquid | blog |
/blog/hello-world | article.liquid | article, blog |
Custom Templates
Create custom templates for specific pages by matching the page slug:
templates/contact.liquid for /contact:
<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:
<!-- 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:
{% editable_canvas content %}
{% repeatable "Section" %}
{% include 'section' %}
{% endrepeatable %}
{% endeditable_canvas %}4. Handle Empty States
Always check if data exists:
{% if products.size > 0 %}
<!-- Show products -->
{% else %}
<p>No products found.</p>
{% endif %}5. Optimize Performance
Use caching for expensive operations:
{% cache 'product-grid', expires_in: 3600 %}
{% for product in products.all %}
{% include 'product-card', product: product %}
{% endfor %}
{% endcache %}Next Steps
- Snippets - Create reusable components
- Pages - Build flexible canvas-based pages
- Webshop - Complete e-commerce templates
- Global Variables - All available variables
Continue to Snippets to learn about creating reusable components.