Getting Started with Nimbu Themes
This guide walks you through creating your first Nimbu theme. You'll build a simple website with a homepage, content pages, and navigation to understand the fundamentals.
Prerequisites
- Access to a Nimbu site (create one at nimbu.io)
- Basic HTML and CSS knowledge
- Familiarity with template languages (helpful but not required)
Understanding the Basics
Before diving in, understand these key concepts:
- Layouts: HTML wrappers that provide the
<head>, navigation, and footer - Templates: Page-specific content that renders inside layouts
- Snippets: Reusable components included in templates
- Liquid: The template language that powers dynamic content
Step 1: Create Your First Layout
Layouts provide the HTML structure for your entire site. Create layouts/default.liquid:
<!DOCTYPE html>
<html lang="{{ locale }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page.title }} - {{ site.name }}</title>
<meta name="description" content="{{ seo.description }}">
{{ "style.css" | stylesheet_tag }}
{{ content_for_header }}
</head>
<body class="template-{{ template }}">
<header>
<div class="container">
<a href="/" class="logo">{{ site.name }}</a>
<nav>
{% nav main, class: "menu" %}
</nav>
</div>
</header>
<main>
{{ content_for_body }}
</main>
<footer>
<div class="container">
<p>© {{ 'now' | date: '%Y' }} {{ site.name }}</p>
</div>
</footer>
{{ "app.js" | javascript_tag }}
</body>
</html>Key Elements Explained
{{ locale }}- Current language code (e.g., "en", "nl"){{ page.title }}- Title of the current page{{ content_for_header }}- Nimbu-injected scripts and meta tags (required){{ content_for_body }}- Where templates render (required){% nav main %}- Renders the "main" menu automatically{{ "style.css" | stylesheet_tag }}- Loads CSS from assets with CDN caching
Step 2: Create Your Homepage Template
Templates define page-specific content. Create templates/index.liquid:
<section class="hero">
<div class="container">
<h1>{% editable_field title %}Welcome to {{ site.name }}{% endeditable_field %}</h1>
<div class="lead">
{% editable_text intro %}
<p>Build amazing websites with Nimbu.</p>
{% endeditable_text %}
</div>
</div>
</section>
<section class="features">
<div class="container">
{% editable_canvas features %}
{% repeatable "Feature Block" %}
{% include 'feature-card' %}
{% endrepeatable %}
{% endeditable_canvas %}
</div>
</section>Editable Regions
{% editable_field %}- Single-line text that editors can change{% editable_text %}- Rich text editor for formatted content{% editable_canvas %}- Drag-and-drop area for repeatable blocks{% repeatable %}- Content blocks editors can add/remove/reorder
Step 3: Create a Reusable Snippet
Snippets make components reusable. Create snippets/feature-card.liquid:
<div class="feature-card">
<div class="icon">
{% editable_file icon, hint: "Upload an icon image" %}
{{ "placeholder-icon.svg" | theme_image_url }}
{% endeditable_file %}
</div>
<h3>{% editable_field heading %}Feature Title{% endeditable_field %}</h3>
<div class="description">
{% editable_text description %}
<p>Describe your feature here.</p>
{% endeditable_text %}
</div>
</div>This snippet is included in the homepage with {% include 'feature-card' %}. Editors can add multiple feature cards through the canvas system.
Step 4: Create a Generic Page Template
For regular content pages, create templates/page.liquid:
<article class="page">
<header class="page-header">
<div class="container">
<h1>{{ page.title }}</h1>
</div>
</header>
<div class="page-content">
<div class="container">
{% editable_canvas content %}
{% repeatable "Text Block" %}
{% include 'text-block' %}
{% endrepeatable %}
{% repeatable "Text & Image" %}
{% include 'text-image' %}
{% endrepeatable %}
{% repeatable "Gallery" %}
{% include 'gallery' %}
{% endrepeatable %}
{% endeditable_canvas %}
</div>
</div>
</article>Create the snippet snippets/text-block.liquid:
<div class="text-block">
{% editable_text content %}
<p>Add your content here...</p>
{% endeditable_text %}
</div>Step 5: Add Basic Styling
Create assets/style.css:
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* Header */
header {
background: #fff;
border-bottom: 1px solid #eee;
padding: 20px 0;
}
header .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 24px;
font-weight: bold;
text-decoration: none;
color: #333;
}
.menu {
display: flex;
list-style: none;
gap: 30px;
}
.menu a {
text-decoration: none;
color: #666;
}
.menu a:hover {
color: #000;
}
/* Hero section */
.hero {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 100px 0;
text-align: center;
}
.hero h1 {
font-size: 48px;
margin-bottom: 20px;
}
.lead {
font-size: 20px;
opacity: 0.9;
}
/* Features */
.features {
padding: 80px 0;
}
.features .container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 40px;
}
.feature-card {
text-align: center;
padding: 30px;
}
.feature-card .icon img {
width: 64px;
height: 64px;
margin-bottom: 20px;
}
.feature-card h3 {
margin-bottom: 15px;
}
/* Page content */
.page-header {
background: #f8f9fa;
padding: 60px 0;
text-align: center;
}
.page-content {
padding: 60px 0;
}
.text-block {
margin-bottom: 30px;
}
/* Footer */
footer {
background: #333;
color: white;
padding: 40px 0;
text-align: center;
margin-top: 80px;
}Step 6: Test Your Theme
- Upload your theme to Nimbu (via CLI or web interface)
- Create pages in the Nimbu admin:
- Homepage (uses
index.liquid) - About page (uses
page.liquid) - Contact page (uses
page.liquid)
- Homepage (uses
- Create a menu named "main" with links to your pages
- Edit content using the inline editor to add feature cards and page content
Next Steps: Adding More Features
Add a Blog
Create templates/blog.liquid to list articles:
<div class="blog">
<div class="container">
<h1>{{ blog.name }}</h1>
<div class="articles">
{% for article in blog.articles %}
<article class="article-card">
<h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p class="meta">{{ article.published_at | localized_date: 'long' }}</p>
<p>{{ article.excerpt }}</p>
<a href="{{ article.url }}">Read more →</a>
</article>
{% endfor %}
</div>
</div>
</div>And templates/article.liquid for individual posts:
<article class="article">
<header class="article-header">
<div class="container">
<h1>{{ article.title }}</h1>
<p class="meta">
{{ article.published_at | localized_date: 'long' }}
{% if article.author %} by {{ article.author }}{% endif %}
</p>
</div>
</header>
<div class="article-content">
<div class="container">
{{ article.content }}
</div>
</div>
</article>Add Multilingual Support
Update your layout to include language switching:
<nav class="language-switcher">
{% for translation in page.translations %}
<a href="{{ translation.url }}"
class="{% if locale == translation.locale %}active{% endif %}">
{{ translation.locale | upcase }}
</a>
{% endfor %}
</nav>Wrap translatable text in {% translate %} tags:
<button>
{% translate 'cta.learn_more', default: 'Learn More' %}
</button>Add a Contact Form
Create a channel named "contact" in Nimbu admin, then use:
{% form channels.contact, class: 'contact-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' %}
{% endform %}Common Patterns
Conditional Content
{% if customer %}
<p>Welcome back, {{ customer.first_name }}!</p>
{% else %}
<a href="/customers/login">Sign In</a>
{% endif %}Looping Through Data
{% for product in products.latest limit: 4 %}
<div class="product-card">
<img src="{{ product.images.first.url | filter, width: '300px' }}" alt="{{ product.name }}">
<h3>{{ product.name }}</h3>
<p>{{ product.price | money_with_currency }}</p>
</div>
{% endfor %}Including Snippets with Parameters
{% include 'product-card', product: featured_product, show_badge: true %}In snippets/product-card.liquid:
<div class="product-card">
{% if show_badge %}<span class="badge">Featured</span>{% endif %}
<h3>{{ product.name }}</h3>
<p>{{ product.price | money_with_currency }}</p>
</div>Best Practices
1. Keep Layouts Simple
Layouts should handle structure, not content. Move content to templates and snippets.
2. Make Content Editable
Use editable regions so clients can update content without code changes:
{% editable_field heading %}Default Text{% endeditable_field %}3. Use Semantic HTML
Choose the right HTML elements for accessibility and SEO:
<article>, <section>, <nav>, <header>, <footer>, <aside>4. Optimize Images
Always resize images using filters:
{{ product.image.url | filter, width: '800px', cropping: 'fill' }}5. Cache Expensive Operations
Use caching for complex queries:
{% cache 'product-grid', expires_in: 3600 %}
{% for product in products.all %}
...
{% endfor %}
{% endcache %}Troubleshooting
Syntax Errors
- Check that all
{% %}tags are properly closed - Ensure quotes match (don't mix ' and ")
- Validate Liquid syntax in the Nimbu editor
Missing Variables
- Check that variables exist before using them:
{% if product %}...{% endif %} - Review available variables in the Global Variables reference
Styling Issues
- Verify asset paths:
{{ "style.css" | stylesheet_tag }} - Check browser console for 404 errors
- Clear Nimbu's CDN cache if assets don't update
Resources
- Key Concepts - Deep dive into layouts, templates, and snippets
- Using Content - Canvas system and editable content
- Using Filters - Transform data with Liquid filters
- Global Variables - Complete variable reference
What's Next?
Now that you have a basic theme, explore:
- Canvas system for drag-and-drop page building
- Channels for custom content types
- E-commerce features for product catalogs and checkout
- Performance optimization with caching and pagination
Continue to Layouts to understand theme structure in depth.