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 + ``DEFERRED`` sentinel for awaitable
   keys + the ``__chirp_defer_pending__`` frozenset (``CHIRP_DEFER_PENDING_KEY``)
3. Yield shell as first chunk (instant first paint)
4. Resolve awaitables concurrently (anyio task group)
5. Determine blocks to re-render:
   a. If ``defer_blocks`` is set, use that list directly
   b. Otherwise, discover via ``block_metadata().depends_on``
      and prune ancestor blocks (strict ``depends_on`` superset)
6. Render each block with full context
7. Yield OOB swap chunks (htmx or <template>+<script>)

Classes

_Deferred 4
Sentinel value for Suspense deferred context keys. Used instead of ``None`` so that templates can …

Sentinel value for Suspense deferred context keys.

Used instead ofNoneso that templates can distinguish "not yet loaded" from "loaded but empty/falsy". Thedeferredkida test ({% if x is deferred %}) checks identity against this singleton.

__bool__ raises TypeError so that bare {% if x %}fails loudly instead of silently treating a pending value as falsy.

Attributes

Name Type Description
_instance _Deferred | None

Methods

Internal Methods 3
__new__ 0 _Deferred
def __new__(cls) -> _Deferred
Returns
_Deferred
__bool__ 0 bool
def __bool__(self) -> bool
Returns
bool
__repr__ 0 str
def __repr__(self) -> str
Returns
str

Functions

format_oob_htmx 4 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, swap: str = 'true', *, wrap: bool = True) -> 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
swap str Default:'true'
wrap bool Default:True
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.

If the block's first child element has the sameidas the target, replaceWithis used (outerHTML-style) to avoid double-nesting. OtherwiseinnerHTMLreplacement is used.

Parameters
Name Type Description
block_html str
target_id str
Returns
str
_render_error_html 7 str
Render error fallback HTML for a failed deferred block. Resolution order: 1. P…
def _render_error_html(env: Environment, *, block_name: str, deferred_key: str, error: BaseException | None, error_template: str | None, error_block: str, suspense_error_block: str | None) -> str

Render error fallback HTML for a failed deferred block.

Resolution order:

  1. Per-routeSuspense(error_block=...)rendered from the global error_template(caller should pass this as suspense_error_block)
  2. Globalerror_template + error_blockfrom AppConfig
  3. Hardcoded default HTML
Parameters
Name Type Description
env Environment
block_name str
deferred_key str
error BaseException | None
error_template str | None
error_block str
suspense_error_block str | None
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.

Parent blocks whosedepends_onis a strict superset of another matched block are pruned — they would re-render the entire section for an OOB target that likely doesn't exist in the DOM.

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]]
_prune_ancestor_blocks 2 list[str]
Drop blocks whose depends_on is a strict superset of another block's. Parent b…
def _prune_ancestor_blocks(blocks: list[str], deps_by_block: dict[str, frozenset[str]]) -> list[str]

Drop blocks whose depends_on is a strict superset of another block's.

Parent blocks in the AST always accumulate the full dependency set of their children. When both a parent (page_content) and a leaf (stats_panel) match a deferred key, the parent's depends_on is a strict superset of the leaf's. Re-rendering the parent as an OOB chunk is expensive and the target id rarely exists in the DOM.

Parameters
Name Type Description
blocks list[str]
deps_by_block dict[str, frozenset[str]]
Returns
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
_close_unstarted_awaitables 1 None
Close coroutine awaitables that validation rejected before scheduling.
def _close_unstarted_awaitables(awaitables: dict[str, Awaitable[Any]]) -> None
Parameters
Name Type Description
awaitables dict[str, Awaitable[Any]]
render_suspense 10 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, oob_registry: OOBRegistry | None = None, fragment_target_registry: FragmentTargetRegistry | None = None, error_template: str | None = None, error_block: str = 'fallback') -> 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
oob_registry OOBRegistry | None

Optional OOB registry for swap/wrap resolution.

Default:None
fragment_target_registry FragmentTargetRegistry | None

Optional fragment target registry for replace-style boosted navigation that must skip outer layouts.

Default:None
error_template str | None Default:None
error_block str Default:'fallback'
Returns
AsyncIterator[str]