Flask Integration

Add a typed Kida component and fragment route to Flask in ten minutes

2 min read 383 words

Add a typed Kida component, a form, and a fragment response to an existing Flask application. Kida requires Python 3.14 or newer.

Coming from Jinja2? Read Coming from Jinja2 first. In particular, Kida's{% set %}is block-scoped rather than leaking into its surrounding scope.

1. Install

uv add flask kida-templates

2. Register Kida and add two routes

init_kida()leaves Flask's Jinja environment intact and adds a separate Kida environment atapp.extensions["kida"] and app.kida_env.

from flask import Flask, request

from kida.contrib.flask import init_kida, render_template

app = Flask(__name__)
kida_env = init_kida(app)


@app.get("/")
def home() -> str:
    return render_template(
        "components.html",
        title="First component",
        summary="Edit me",
    )


@app.post("/preview")
def preview() -> str:
    template = kida_env.get_template("components.html")
    return template.render_block(
        "preview",
        title=request.form.get("title", ""),
        summary=request.form.get("summary", ""),
    )

render_template()reads Kida from Flask's current application context. Pass template_folder= to init_kida() to override app.template_folder, or pass normalEnvironment options such as cache_size=400.

3. Create the typed component

Save this astemplates/components.html:

{% def panel(title: str) %}
<section class="panel">
  <h1>{{ title }}</h1>
  {% slot %}
</section>
{% enddef %}

{% def text_field(name: str, label: str, value: str = "") %}
<label>
  {{ label }}
  <input name="{{ name }}" value="{{ value }}">
</label>
{% enddef %}

{% block form %}
{% call panel("Component form") %}
<form method="post" action="/preview">
  {{ text_field("title", "Title", title) }}
  {{ text_field("summary", "Summary", summary) }}
  <button type="submit">Preview</button>
</form>
{% endcall %}
{% endblock %}

{% block preview %}
<article id="preview">
  <h2>{{ title }}</h2>
  <p>{{ summary }}</p>
</article>
{% endblock %}

The component call is checked against the typed def signature. The / route renders the full template;/previewrenders only the named block for HTMX, Turbo, or another HTML-over-the-wire client. Both paths use the same autoescaping rules.

4. Run it

uv run flask --app app run --debug

The repository includes a runnable, smoke-tested version at examples/flask_components.

Add filters and globals

Register application helpers on the environment returned byinit_kida():

from flask import url_for

kida_env.add_global("url_for", url_for)


@kida_env.filter()
def currency(value: float) -> str:
    return f"${value:,.2f}"

Production setup

Setauto_reload=Falseoutside development and size the template cache for your application:

kida_env = init_kida(app, auto_reload=False, cache_size=400)

Next steps