Block Syntax

Control flow blocks, endings, and pattern matching

5 min read 911 words

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
  • Needunlesssemantics

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