The Problem
Traditional template rendering waits for all data before sending anything. If your dashboard fetches stats, recent activity, and notifications, the user stares at a blank page until the slowest query finishes.
The Solution
Streamrenders template sections as they complete. The browser receives the page shell immediately and content fills in progressively:
from chirp import Stream
@app.route("/dashboard")
async def dashboard():
return Stream("dashboard.html",
header=get_header(), # Available immediately
stats=await load_stats(), # Streams when ready
activity=await load_activity(), # Streams when ready
)
The HTTP response uses chunked transfer encoding. The browser renders progressively as chunks arrive -- no JavaScript loading states, no skeleton screens.
How It Works
- Kida's compiler generates a streaming renderer alongside the standard renderer (same compilation pass, no performance impact)
- When
Streamis returned, Chirp sends the response withTransfer-Encoding: chunked - Kida's
render_stream()yields HTML chunks as template sections complete - Each chunk is sent to the client immediately via ASGI body messages
- The browser renders each chunk as it arrives
Template: <html> ... {% block header %} ... {% block stats %} ... {% block activity %}
Chunks: ────────→ ──────────────────→ ──────────────→ ─────────────────────────→
Time: 0ms 50ms 200ms 800ms
Template Structure for Streaming
Design templates with independent sections that can render in any order:
{# dashboard.html #}
{% extends "base.html" %}
{% block content %}
<header>{{ header }}</header>
<section id="stats">
{% block stats %}
{% for stat in stats %}
<div class="stat">{{ stat.label }}: {{ stat.value }}</div>
{% endfor %}
{% endblock %}
</section>
<section id="activity">
{% block activity %}
{% for event in activity %}
<div class="event">{{ event.description }}</div>
{% endfor %}
{% endblock %}
</section>
{% endblock %}
Error Handling
If an error occurs mid-stream, Chirp injects an HTML comment with the error details and closes the stream gracefully:
<!-- Stream error: DatabaseConnectionError: connection timed out -->
The already-sent content remains visible. This is better than a full-page error for partial failures.
StreamingResponse
Under the hood,Stream produces a StreamingResponse -- a peer to Responsewith the same chainable API:
# StreamingResponse supports .with_*() methods
return Stream("dashboard.html", data=data)
# Internally becomes:
# StreamingResponse(generator, status=200, headers=...)
Middleware can add headers to streaming responses the same way as regular responses.
When to Use Streaming
UseStreamwhen:
- A page has multiple independent data sources with varying load times
- You want to show the page layout immediately
- Time-to-first-byte matters more than total render time
- You are building dashboards, feeds, or data-heavy pages
UseTemplatewhen:
- All data is available quickly
- The template is simple
- You need the complete response for caching or processing
Next Steps
- Server-Sent Events -- Real-time push updates
- Return Values -- All return types
- Rendering -- Standard template rendering