Module

templating.suspense

Suspense-style streaming — shell first, deferred blocks via OOB.

Renders a page shell immediately with skeleton/fallback content for blocks whose data is still loading, then streams in the real content as each async source resolves.

Two delivery strategies (auto-selected by the negotiation layer):

  • htmx navigations: deferred blocks arrive ashx-swap-oob elements that htmx processes automatically.
  • Initial page loads:<template> + inline <script>pairs swap content into place without any framework dependency.

Pipeline::

Suspense("dashboard.html",
    header=site_header(),    # sync — available in the shell
    stats=load_stats(),      # awaitable — deferred
    feed=load_feed(),        # awaitable — deferred
)

1. Separate sync vs. awaitable context values
2. Render shell with sync context + None for awaitable keys
3. Yield shell as first chunk (instant first paint)
4. Resolve awaitables concurrently (anyio task group)
5. For each resolved key, find affected blocks via block_metadata
6. Render each block with full context
7. Yield OOB swap chunks (htmx or <template>+<script>)

Functions

format_oob_htmx 2 str
Wrap rendered block HTML as an htmx OOB swap element. htmx scans the response …
def format_oob_htmx(block_html: str, target_id: str) -> str

Wrap rendered block HTML as an htmx OOB swap element.

htmx scans the response body for elements withhx-swap-oob and swaps them into the page byid.

Parameters
Name Type Description
block_html str
target_id str
Returns
str
format_oob_script 2 str
Wrap rendered block HTML as a ```` + ```` pair. Used for initial page loads wh…
def format_oob_script(block_html: str, target_id: str) -> str

Wrap rendered block HTML as a<template> + <script>pair.

Used for initial page loads where htmx OOB is not available. The inline script moves template content into the target element.

Parameters
Name Type Description
block_html str
target_id str
Returns
str
_find_deferred_blocks 3 dict[str, list[str]]
Map each deferred context key to the template blocks that depend on it. Uses k…
def _find_deferred_blocks(env: Environment, template_name: str, deferred_keys: set[str]) -> dict[str, list[str]]

Map each deferred context key to the template blocks that depend on it.

Uses kida'sblock_metadata()static analysis to find blocks whosedepends_onset intersects with the deferred keys.

Returns{context_key: [block_name, ...]}— a key may affect multiple blocks, and a block may appear under multiple keys (de-duplicated during rendering).

Parameters
Name Type Description
env Environment
template_name str
deferred_keys set[str]
Returns
dict[str, list[str]]
_should_wrap_in_layouts 2 bool
Return True if the shell should be wrapped in the layout chain.
def _should_wrap_in_layouts(layout_chain: Any, request: Any) -> bool
Parameters
Name Type Description
layout_chain Any
request Any
Returns
bool
render_suspense 6 AsyncIterator[str]
Render a ``Suspense`` return value as an async chunk stream.
async
async def render_suspense(env: Environment, suspense: Suspense, *, is_htmx: bool = False, layout_chain: Any = None, layout_context: dict[str, Any] | None = None, request: Any = None) -> AsyncIterator[str]
Parameters
Name Type Description
env Environment

Kida template environment.

suspense Suspense

TheSuspensereturn value from a route handler.

is_htmx bool

IfTrue, use hx-swap-oob formatting. If False, use <template> + <script>pairs.

Default:False
layout_chain Any

Optional layout chain to wrap the shell in.

Default:None
layout_context dict[str, Any] | None

Context for layout templates (when layout_chain used).

Default:None
request Any

Request for fragment detection (when layout_chain used).

Default:None
Returns
AsyncIterator[str]