Streaming

Stream template output as chunks for HTTP responses and large pages

3 min read 502 words

Kida can render templates as a stream of string chunks instead of building the entire output in memory. This is useful for chunked HTTP responses, Server-Sent Events, and large pages.

Sync Streaming

Userender_stream()for synchronous streaming:

from kida import Environment

env = Environment()
template = env.from_string("""
<ul>
{% for item in items %}
    <li>{{ item }}</li>
{% end %}
</ul>
""")

for chunk in template.render_stream(items=["a", "b", "c"]):
    send_to_client(chunk)

Each statement-level boundary produces a chunk. Static content, variable output, and control flow transitions all yield independently.

RenderedTemplate

RenderedTemplate is a lazy iterable wrapper around render_stream():

from kida import RenderedTemplate

rendered = RenderedTemplate(template, {"items": data})

# Iterate to get chunks on demand
for chunk in rendered:
    response.write(chunk)

# Or convert to string (consumes all chunks)
html = str(rendered)

Async Streaming

Userender_stream_async() to stream templates that contain {% async for %} or {{ await }}constructs:

async def stream_response():
    template = env.get_template("page.html")
    async for chunk in template.render_stream_async(items=async_data()):
        yield chunk

render_stream_async()also works on sync templates — it wraps the sync stream, so you can use a single code path:

async for chunk in template.render_stream_async(**context):
    await response.write(chunk)

Block Streaming

Stream a single block instead of the full template:

# Sync
for chunk in template.render_block_stream("content", **context):
    send(chunk)

# Async
async for chunk in template.render_block_stream_async("content", **context):
    await send(chunk)

Framework Integration

Starlette / FastAPI

from starlette.responses import StreamingResponse

async def page(request):
    template = env.get_template("page.html")

    async def generate():
        async for chunk in template.render_stream_async(items=fetch_items()):
            yield chunk.encode()

    return StreamingResponse(generate(), media_type="text/html")

Flask

from flask import Response

@app.route("/page")
def page():
    template = env.get_template("page.html")
    return Response(
        template.render_stream(items=get_items()),
        content_type="text/html",
    )

What Streams

All template constructs work in streaming mode:

Construct Behavior
Static text Yielded as-is
{{ expr }} Yielded after evaluation
{% for %} / {% async for %} Each iteration yields independently
{% if %} / {% match %} Chosen branch yields
{% extends %} / {% block %} Parent/child blocks stream
{% include %} Included template streams inline
{% capture %} / {% spaceless %} Buffers internally, yields processed result
{% cache %} Buffers internally, yields cached result

Choosing a Rendering Method

Method Use Case
render() Fastest — builds full string in memory
render_stream() Sync streaming — chunked HTTP, large pages
render_stream_async() Async streaming — async iterables,awaitin templates
render_async() Async buffered —awaitsupport, full string output

For most pages,render()is the best choice. Use streaming when the output is large or you want to start sending bytes before the template finishes rendering.

See Also