Python component framework for HTML — typed props, named slots, scoped state, error boundaries, zero JavaScript
Project description
)彡 Kida
A Python component framework for HTML — typed props, named slots, scoped state, error boundaries, and zero JavaScript.
Kida compiles templates to Python AST, renders to HTML/terminal/markdown, and scales across cores on free-threaded Python 3.14t. Zero runtime dependencies.
Quick Start
pip install kida-templates
{% def card(title: str, variant: str = "default") %}
<article class="card card--{{ variant }}">
<h3>{{ title }}</h3>
<div class="actions">{% slot header_actions %}</div>
<div class="body">{% slot %}</div>
</article>
{% end %}
{% call card("Settings", variant="elevated") %}
{% slot header_actions %}<button>Save</button>{% end %}
<p>Configure your preferences.</p>
{% end %}
from kida import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/"))
template = env.get_template("page.html")
html = template.render(title="Hello")
Component Model
Kida gives you the composition patterns of modern frontend frameworks, without a build step.
| Feature | Syntax |
|---|---|
| Typed props | {% def card(title: str, count: int = 0) %} |
| Named slots | {% slot header %} / {% slot %} (default) |
| Conditional slots | has_slot("footer") |
| Scoped slots (data up) | {% slot row let:item=item %} |
| Slot forwarding | {% yield name %} |
| Context propagation | {% provide theme = "dark" %} / consume("theme") |
| Error boundaries | {% try %}...{% fallback error %}...{% end %} |
| Co-located styles | {% push "styles" %} / {% stack "styles" %} |
| Pattern matching | {% match status %}{% case "active" %}...{% end %} |
| Block-scoped variables | {% set %} (scoped) / {% let %} (template-wide) / {% export %} |
Static Validation
kida check templates/ --validate-calls
card.html:14: type: badge() param 'count' expects int, got str ("five")
dashboard.html:8: Call to 'card' — unknown params: titl
Catches unknown params, missing required params, and literal type mismatches at check time.
Component Discovery
kida components templates/
# components/card.html
# def card(title: str, subtitle: str | None = None)
# slots: header_actions, footer
#
# components/button.html
# def button(label: str, variant: str = "primary")
# slots: (none)
#
# 2 component(s) found.
Introspection API
template = env.get_template("components/card.html")
meta = template.def_metadata()
card = meta["card"]
print(card.params) # (DefParamInfo(name='title', annotation='str', ...), ...)
print(card.slots) # ('header_actions', 'footer')
print(card.has_default_slot) # True
Why Not Jinja2?
| Jinja2 | Kida | |
|---|---|---|
| Typed parameters | No | param: str | None |
| Named slots | No (caller() only) |
{% slot name %} |
| Scoped variables | set leaks out of blocks |
set is block-scoped |
| Context propagation | Prop drilling | provide / consume |
| Error boundaries | No | {% try %}...{% fallback %} |
| Component styles | Disconnected CSS files | {% push "styles" %} |
| Call-site validation | Runtime errors | Compile-time checks |
| Component discovery | Read every file | kida components CLI |
| Block rendering | No | render_block() for HTMX partials |
| Streaming | Limited | render_stream() for chunked HTTP/SSE |
| Free-threading | No | GIL-free on Python 3.14t |
Render Surfaces
One template syntax, four outputs.
HTML
from kida import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/"))
html = env.get_template("page.html").render(title="Hello")
Terminal
from kida.terminal import terminal_env
env = terminal_env()
template = env.from_string("""
{{ "Deploy Status" | bold | cyan }}
{{ hr(40) }}
{% for svc in services %}
{{ svc.name | pad(20) }}{{ svc.status | badge }}
{% end %}
""")
print(template.render(services=[
{"name": "api", "status": "pass"},
{"name": "worker", "status": "fail"},
]))
Markdown
from kida.markdown import markdown_env
env = markdown_env()
md = env.from_string("# {{ title }}\n\n{{ body }}").render(
title="Report", body="All tests passed."
)
CI Reports (GitHub Action)
Turn pytest, coverage, ruff, and other tool output into step summaries and PR comments.
- uses: lbliii/kida@v0.3.3
with:
template: pytest
data: results.xml
data-format: junit-xml
post-to: step-summary,pr-comment
Built-in templates for pytest, coverage, ruff, ty, jest, gotest, and sarif. Full action docs →
More Features
Template Inheritance
{# base.html #}
<!DOCTYPE html>
<html>
<body>{% block content %}{% end %}</body>
</html>
{# page.html #}
{% extends "base.html" %}
{% block content %}<h1>{{ title }}</h1>{% end %}
Regions (Parameterized Blocks)
{% region sidebar(current_path="/") %}
<nav>{{ current_path }}</nav>
{% end %}
{{ sidebar(current_path="/about") }}
Regions are blocks (for render_block()) and callables (for inline use). Ideal for HTMX OOB swaps.
Pattern Matching & Null Safety
{% match status %}
{% case "active" %}Active{% case "pending" %}Pending{% case _ %}Unknown
{% end %}
{{ user.nickname ?? user.name ?? "Anonymous" }}
{{ config?.database?.host }}
{{ data ?|> parse ?|> validate ?|> render }}
Streaming & Block Rendering
# Stream chunks as they render
for chunk in template.render_stream(items=large_list):
response.write(chunk)
# Render a single block (HTMX partials)
html = template.render_block("content", title="Hello")
# Compose layouts with pre-rendered blocks
html = layout.render_with_blocks({"content": inner_html}, title="Page")
Compile-Time Optimization
template = env.from_string(source, static_context={
"site": site_config, "settings": app_settings,
})
html = template.render(page_title="Home", items=page_items)
67 pure filters evaluated at compile time. Dead branches removed. Component inlining for small defs with constant args. Use kida render template.html --explain to see active optimizations.
Free-Threading
All public APIs are safe under PYTHON_GIL=0 (Python 3.14t, PEP 703). Templates compile to immutable AST, rendering uses thread-local accumulators, and the Environment uses copy-on-write. Scales linearly with cores.
Framework Integration
# Flask
from kida.contrib.flask import KidaFlask
kida = KidaFlask(app)
# Starlette / FastAPI
from kida.contrib.starlette import KidaStarlette
templates = KidaStarlette(directory="templates")
# Django
TEMPLATES = [{"BACKEND": "kida.contrib.django.KidaDjango", ...}]
CLI
kida render template.txt --data context.json
kida check templates/ --validate-calls --a11y --typed
kida components templates/ --json
kida fmt templates/
kida extract templates/ -o messages.pot
Upgrading
Moving from 0.6.x? See the Upgrade to 0.7 tutorial for the strict_undefined=True migration patterns and the new null-safe idioms (?., ?[, | get).
The Bengal Ecosystem
Kida is part of a pure-Python stack built for 3.14t free-threading.
| ᓚᘏᗢ | Bengal | Static site generator | Docs |
| ∿∿ | Purr | Content runtime | — |
| ⌁⌁ | Chirp | Web framework | Docs |
| =^..^= | Pounce | ASGI server | Docs |
| )彡 | Kida | Component framework | Docs |
| ฅᨐฅ | Patitas | Markdown parser | Docs |
| ⌾⌾⌾ | Rosettes | Syntax highlighter | Docs |
| ᓃ‿ᓃ | Milo | Terminal UI framework | Docs |
License
MIT License — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kida_templates-0.8.0.tar.gz.
File metadata
- Download URL: kida_templates-0.8.0.tar.gz
- Upload date:
- Size: 590.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c80c0cbb53fa88723454c79ca4485fcc85f6ebaa920eb675ba1866a9e461168c
|
|
| MD5 |
82ae61cee6f0d50be95e3845ff16c2ac
|
|
| BLAKE2b-256 |
5881ef95a7342505b682d4c0948901096bca3a760fd6332ad8e4dab2808015bb
|
Provenance
The following attestation bundles were made for kida_templates-0.8.0.tar.gz:
Publisher:
python-publish.yml on lbliii/kida
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kida_templates-0.8.0.tar.gz -
Subject digest:
c80c0cbb53fa88723454c79ca4485fcc85f6ebaa920eb675ba1866a9e461168c - Sigstore transparency entry: 1373109937
- Sigstore integration time:
-
Permalink:
lbliii/kida@4f4b68631c757677b1f4b6891d200c9ef611019c -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/lbliii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@4f4b68631c757677b1f4b6891d200c9ef611019c -
Trigger Event:
release
-
Statement type:
File details
Details for the file kida_templates-0.8.0-py3-none-any.whl.
File metadata
- Download URL: kida_templates-0.8.0-py3-none-any.whl
- Upload date:
- Size: 408.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01f7240f7228ebda14a66c367071fe6e8a47b0ef6775f84006ae1c1f5dfadbc3
|
|
| MD5 |
249bdd790fffd3a74932dabfcfb9c50c
|
|
| BLAKE2b-256 |
392a8c7345b3449ea92aa45820d47fccdfd51a7a1a04a7e4ba9a9d9bc4d02560
|
Provenance
The following attestation bundles were made for kida_templates-0.8.0-py3-none-any.whl:
Publisher:
python-publish.yml on lbliii/kida
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kida_templates-0.8.0-py3-none-any.whl -
Subject digest:
01f7240f7228ebda14a66c367071fe6e8a47b0ef6775f84006ae1c1f5dfadbc3 - Sigstore transparency entry: 1373110038
- Sigstore integration time:
-
Permalink:
lbliii/kida@4f4b68631c757677b1f4b6891d200c9ef611019c -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/lbliii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@4f4b68631c757677b1f4b6891d200c9ef611019c -
Trigger Event:
release
-
Statement type: