Template Formatter

Opinionated template formatting with kida fmt

3 min read 598 words

Kida includes an opinionated template formatter that enforces consistent whitespace, indentation, and tag spacing across your templates. No other Python template engine ships with a built-in formatter.

Quick Start

Format all templates in a directory:

kida fmt templates/

Format a single file:

kida fmt templates/page.html

Check formatting without modifying files (useful in CI):

kida fmt --check templates/

What It Does

The formatter applies six rules to every template:

Rule Before After
Tag spacing {%if x%} {% if x %}
Output spacing {{ expr }} {{ expr }}
Comment spacing {#note#} {# note #}
Block indentation Flat or inconsistent Indented by configured amount
Trailing whitespace <div> <div>
Blank line normalization 3+ consecutive blank lines Max 1 consecutive blank line
Final newline Missing or present Always present

Tag Spacing

The formatter normalizes spacing inside all three tag types. Whitespace control markers (-) are preserved:

{# Before #}
{%-  if user  -%}{{user.name}}{%endif%}

{# After #}
{%- if user -%}{{ user.name }}{% endif %}

Block Indentation

Block bodies are indented by the configured amount (default: 2 spaces). The formatter recognizes all Kida block-opening keywords:

if, for, block, while, with, def, region, call, capture, cache, filter, match, spaceless, embed, raw, push, globals, imports, unless, fragment

Continuation keywords (else, elif, empty, case) are aligned with their opening tag, and their bodies are indented:

{% if user.is_admin %}
  <span>Admin</span>
{% elif user.is_staff %}
  <span>Staff</span>
{% else %}
  <span>User</span>
{% endif %}

Configuration

Theformat_templatefunction accepts three options:

Parameter Type Default Description
indent int 2 Number of spaces per indentation level
max_blank_lines int 1 Maximum consecutive blank lines allowed
normalize_tag_spacing bool True Normalize spacing inside{% %}, {{ }}, and {# #}tags
from kida.formatter import format_template

formatted = format_template(
    source,
    indent=4,               # 4 spaces per level
    max_blank_lines=2,       # Allow up to 2 consecutive blank lines
    normalize_tag_spacing=True,
)

Programmatic API

Importformat_templatedirectly for build scripts, pre-commit hooks, or editor integrations:

from kida.formatter import format_template

source = open("templates/page.html").read()
formatted = format_template(source)

if source != formatted:
    open("templates/page.html", "w").write(formatted)
    print("Formatted page.html")

CI Integration

Use the--checkflag to fail CI when templates are not formatted. This does not modify any files — it only reports whether formatting changes are needed:

kida fmt --check templates/

A non-zero exit code means at least one file needs formatting. Pair this with your existing linting step:

# GitHub Actions example
- name: Check template formatting
  run: kida fmt --check templates/

Before and After

Inconsistent spacing and flat indentation

Before:

{%block sidebar%}
<aside>
{%if categories%}
<ul>
{%for cat in categories%}
<li>{{cat.name}}</li>
{%endfor%}
</ul>
{%endif%}
</aside>
{%endblock%}

After:

{% block sidebar %}
  <aside>
    {% if categories %}
      <ul>
        {% for cat in categories %}
          <li>{{ cat.name }}</li>
        {% endfor %}
      </ul>
    {% endif %}
  </aside>
{% endblock %}

Whitespace control markers preserved

Before:

{%-  for item in items  -%}
{{-item.name-}}
{%-endfor-%}

After:

{%- for item in items -%}
  {{- item.name -}}
{%- endfor -%}

See Also