Kida uses{% end %}for all block endings and adds pattern matching for cleaner conditionals.
Unified Block Endings
Kida uses{% end %}for all block endings, eliminating the need to remember specific closing tags:
{% if page.draft %}
<span class="draft">Draft</span>
{% end %}
{% for post in posts %}
<article>{{ post.title }}</article>
{% end %}
{% block content %}
{{ page.content | safe }}
{% end %}
Compatibility: Specific endings like{% endif %},{% endfor %},{% endwhile %}, and{% endblock %}are also accepted for Jinja2 compatibility. Use{% end %}for consistency.
Conditionals
If/Elif/Else
Standard conditional blocks:
{% if page.draft %}
<span class="badge">Draft</span>
{% elif page.scheduled %}
<span class="badge">Scheduled</span>
{% else %}
<span class="badge">Published</span>
{% end %}
Unless
Use{% unless %}for negated conditions (Kida-native):
{% unless page.hidden %}
<article>{{ page.content | safe }}</article>
{% end %}
{% unless user.logged_in %}
<a href="/login">Sign in</a>
{% else %}
<span>Welcome, {{ user.name }}</span>
{% end %}
Loops
For Loops
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt }}</p>
</article>
{% end %}
Loop Variables
Access iteration metadata via theloopobject:
{% for item in items %}
{{ loop.index }} {# 1-indexed: 1, 2, 3... #}
{{ loop.index0 }} {# 0-indexed: 0, 1, 2... #}
{{ loop.first }} {# true on first iteration #}
{{ loop.last }} {# true on last iteration #}
{{ loop.length }} {# total items in sequence #}
{{ loop.revindex }} {# reverse 1-indexed: length...3, 2, 1 #}
{{ loop.revindex0 }} {# reverse 0-indexed: length-1...2, 1, 0 #}
{{ loop.previtem }} {# previous item (None on first) #}
{{ loop.nextitem }} {# next item (None on last) #}
{{ loop.cycle('odd', 'even') }} {# alternates values #}
{% end %}
Empty Clause
Render content when the iterable is empty:
{% for post in posts %}
<article>{{ post.title }}</article>
{% empty %}
<p>No posts found.</p>
{% end %}
Note: Kida uses{% empty %}(not{% else %}) for empty iterables, matching Jinja2 behavior.
Inline Filter
Filter items directly in the loop declaration:
{% for post in posts if post.published %}
<article>{{ post.title }}</article>
{% end %}
While Loops
Kida adds{% while %}loops (not available in Jinja2):
{% let counter = 0 %}
{% while counter < 5 %}
<p>Count: {{ counter }}</p>
{% let counter = counter + 1 %}
{% end %}
Alternative ending: You can also use{% endwhile %}instead of{% end %}.
Loop Control
{% for item in items %}
{% if item.hidden %}
{% continue %}
{% end %}
{% if item.is_final %}
{{ item.title }}
{% break %}
{% end %}
{{ item.title }}
{% end %}
Pattern Matching
Replace longif/elifchains with{% match %}:
{% match page.type %}
{% case "blog" %}
<i class="icon-pen"></i> Blog Post
{% case "doc" %}
<i class="icon-book"></i> Documentation
{% case "tutorial" %}
<i class="icon-graduation-cap"></i> Tutorial
{% case _ %}
<i class="icon-file"></i> Page
{% end %}
The{% case _ %}pattern matches anything (default fallback).
Nested Pattern Matching
{% match page.type %}
{% case "blog" %}
{% match page.format %}
{% case "standard" %}
<article class="blog-standard">{{ page.content | safe }}</article>
{% case "longform" %}
<article class="blog-longform">{{ page.content | safe }}</article>
{% case _ %}
<article class="blog-default">{{ page.content | safe }}</article>
{% end %}
{% case "doc" %}
<article class="doc">{{ page.content | safe }}</article>
{% case _ %}
<article>{{ page.content | safe }}</article>
{% end %}
When to Use Pattern Matching
Use{% match %} when:
- Checking the same variable against multiple discrete values
- You have 3+ cases
- Cases are strings, numbers, or simple patterns
Use{% if %}/{% elif %} when:
- Complex boolean expressions (
if x > 10 and y < 5) - Range checks or comparisons
- Multiple variable conditions
- Need
unlesssemantics
Range Literals
Kida provides cleaner range syntax:
{# Inclusive range: 1, 2, 3, 4, 5 #}
{% for i in 1..5 %}
{{ i }}
{% end %}
{# Exclusive range: 1, 2, 3, 4 #}
{% for i in 1...5 %}
{{ i }}
{% end %}
{# Range with step: 0, 2, 4, 6, 8, 10 #}
{% for i in 0..10 by 2 %}
{{ i }}
{% end %}
Complete Example
Combining multiple block types:
{% extends "baseof.html" %}
{% block content %}
{% match page.type %}
{% case "blog" %}
<article class="blog-post">
<header>
<h1>{{ page.title }}</h1>
{% match page.status %}
{% case "published" %}
<span class="status">Published</span>
{% case "draft" %}
<span class="status draft">Draft</span>
{% case _ %}
<span class="status">Unknown</span>
{% end %}
</header>
<div class="content">
{{ page.content | safe }}
</div>
{% unless page.tags | length == 0 %}
<footer>
{% for tag in page.tags if tag.public %}
<a href="{{ tag_url(tag) }}" class="tag">{{ tag }}</a>
{% empty %}
<span>No public tags</span>
{% end %}
</footer>
{% end %}
</article>
{% case _ %}
<article>
<h1>{{ page.title }}</h1>
{{ page.content | safe }}
</article>
{% end %}
{% end %}
This example demonstrates:
- Unified
{% end %}syntax - Nested pattern matching
{% unless %}for negated conditions{% for %}with inline filter (if tag.public){% empty %}clause for empty iterables