Editable Content, Forms & Data Patterns
Build author-friendly content blocks, dynamic forms, and efficient data queries in Nimbu themes.
Editable Regions
Nimbu’s editable tags let content authors customize sections of a page without touching code. Each tag shares common options:
label– Human-friendly label shown in the backend.hint– Helper text for editors.assign– Skip rendering and assign the value to a variable for later use.
| Tag | Purpose | Example |
|---|---|---|
{% editable_field %} | Single-line string input. | {% editable_field 'hero_title', label: 'Hero title' %}Welcome{% endeditable_field %} |
{% editable_text %} | Rich text/WYSIWYG content. | {% editable_text 'hero_body' %}<p>Intro copy</p>{% endeditable_text %} |
{% editable_file %} | Upload images or documents. | <img src="{% editable_file 'hero_image' %}https://placehold.it/600x400{% endeditable_file %}" alt="" /> |
{% editable_select %} | Dropdown with predefined options. Provide pipes-separated lists in options: and labels:. | {% editable_select 'cta_style', options: 'ghost|solid', labels: 'Ghost|Solid' %}ghost{% endeditable_select %} |
{% editable_switch %} | Boolean toggle (checkbox). | {% editable_switch 'show_banner' %}true{% endeditable_switch %} |
{% editable_reference %} | Reference objects (channels, products, collections, customers, menus, pages). | {% editable_reference 'Featured Product', to: 'products', assign: 'featured_product' %}{% endeditable_reference %} |
Grouping & Repeatables
{% editable_group 'Footer' %}– Collapses related fields under a named group. You can add groups for settings (group: 'settings') and display toggles. Nest canvases or repeatables inside groups.{% editable_canvas 'HeroCanvas' %}– Defines a canvas container whose layout authors can manage via the backend.{% repeatable 'Testimonials' %}– Allows editors to add, reorder, or remove repeated blocks. Combine with editable fields inside to capture structured content.
Example:
{% editable_group 'Hero' %}
{% editable_field 'title', label: 'Heading' %}Our Story{% endeditable_field %}
{% repeatable 'Slides' %}
{% editable_field 'slide_title' %}{% endeditable_field %}
{% editable_text 'slide_copy' %}{% endeditable_text %}
{% endrepeatable %}
{% endeditable_group %}Copywriting & Localization
- Wrap strings in
{% translate %}with a descriptive key and fallback:{% translate 'checkout.free_shipping_banner', default: 'Free shipping on orders over %{threshold}', threshold: '50€' %} - Define translation keys in the Copywriting UI (e.g.
locale.en,locale.fr) to display localized language names. - Use
locale_url_prefixand the{% localized_path 'nl' %}tag when linking to localized pages. - Access per-page locale availability via
page.available_locales,page.available_in_locale?, andpage.translations(each entry haslocale,language,localized_language,url).
Channel Queries & Data Patterns
Scoping Data
Restrict datasets server-side with the {% scope %} tag:
{% scope (published == true) and (publish_at <= now) %}
{% for article in blogs.news.articles %}
{{ article.title }}
{% endfor %}
{% endscope %}Supported operators include ==, !=, <, >, <=, >=, in, nin, exists, contains, start, end, and regex. Nest expressions with parentheses and combine with and / or for complex logic.
Dynamic scopes are possible by capturing the expression:
{% capture filter_expression %}(price > {{ settings.price_floor }}) and (status == 'active'){% endcapture %}
{% scope {{ filter_expression }} %}
...
{% endscope %}Sorting & Pagination
Override default sorted order with {% sort %} inside a scope, or use the sort/numeric_sort filters:
{% sort published_at desc %}
{% for post in blogs.news.articles %}
...
{% endfor %}
{% endsort %}Paginate long lists:
{% paginate channels.events by 20 %}
{% for event in paginate.collection %}
...
{% endfor %}
{{ paginate | default_pagination, previous_label: '«', next_label: '»' }}
{% endpaginate %}Loop Caching
Use the enhanced for tag with cache: to memoize expensive loops. Combine locale strings into the cache key to avoid mixing localized markup.
{% capture cache_key %}hero-{{ channels.hero.updated_at }}-{{ locale }}{% endcapture %}
{% for slide in channels.hero, cache: cache_key %}
...
{% endfor %}Forms & User Input
The form subsystem auto-wires CSRF tokens, validation errors, and redirect handling. A standard channel form looks like this:
{% form channels.contact, class: 'contact-form' %}
{% input 'name', label: 'Name', required: true %}
{% input 'email', as: 'email', label: 'Email', required: true %}
{% input 'message', as: 'text', label: 'Message', rows: 6 %}
{% submit_tag 'Send', class: 'btn btn-primary' %}
{% endform %}Collection Sources
Populate select or radio fields from dynamic data:
- Channel field options:
{% input 'event', as: 'select', collection: channels.events %}. - Customer metadata:
{% input 'title', as: 'select', collection: config.customers.title_items %}. - Static all-country list:
{% input 'country', as: 'select', collection: config.countries_from_the_world %}.
Redirects & Errors
- Include
<input type="hidden" name="redirect_after_submit" value="/thank-you" />(or the equivalenthidden_fieldtag) to redirect on success. - Validation errors render automatically with
.inline-errorclasses and are exposed viaform_model.errors. - Access pending form values through
form_modelinside the form block, even when validations fail.
Custom Layouts
Use {% input_tag_template 'boolean, checkbox' %} to override markup for specific input types. The input_tag helper inside the template exposes id, name, label, value, collection, selected_values, and multiple so you can output custom HTML structures (e.g., Bootstrap custom controls).
Backend Actions & Login
{% form login %}wraps the built-in login form. Combine with{% login_with provider: 'facebook' %}links for social authentication.{% form channels.<slug>, layout: 'bootstrap4' %}selects built-in form layouts.
Content Reuse & Caching
{% consume %}allows you to capture content in one place and reuse it. Combine with{% cache %}when the generated block is expensive to produce.{% capture %}with dynamic expressions is useful for building cache keys and query strings.
Multi-language Page Availability
For pages with restricted locales (configured per-page since Nov 21, 2017), leverage:
{% if page and page.available_in_locale? == false %}
<p>{% translate 'locale.page_not_available', default: 'Sorry, this page is not available in this language.' %}</p>
<ul>
{% for translation in page.translations %}
<li><a href="{{ translation.url }}">{{ translation.language }}</a></li>
{% endfor %}
</ul>
{% else %}
{{ content_for_body }}
{% endif %}This ensures graceful fallbacks when content is not localized yet.
Checklist for Author-Friendly Templates
- Provide clear
labelandhintstrings for every editable field. - Use repeatables for lists, testimonials, and cards to keep content flexible.
- Scope channel queries to avoid pulling unnecessary records, especially on homepages.
- Cache fragments that aggregate multiple channels or products to reduce rendering time.
- Expose key configuration options (colors, CTA labels, toggles) as editables instead of hard-coding them.
With these patterns you can build themes that empower editors while keeping Liquid logic maintainable. Continue to the Cloud Code section when you are ready to enhance storefronts with server-side logic.