Skip to content

Suspense

wybthon.suspense

suspense

Suspense and SuspenseList for rendering fallback UI during async loading.

Suspense tracks resources automatically, matching SolidJS: any Resource read (called) under the boundary while it's still "pending" registers itself, and the boundary shows its fallback until every registered resource resolves. No resources prop wiring is needed.

Refetches don't re-trigger the boundary. A resource that already has data enters the "refreshing" state and keeps serving its previous value, so content stays visible during reloads.

SuspenseList coordinates multiple Suspense boundaries beneath it, controlling the order their contents reveal (reveal_order) and how many fallbacks show at once (tail).

Example
user = create_resource(fetch_user)

Suspense(
    fallback=lambda: p("Loading..."),
    children=[div(lambda: (user() or {}).get("name", ""))],
)
See Also

Functions:

Name Description
Suspense

Show a fallback while resources read under the boundary are pending.

SuspenseList

Coordinate the reveal order of multiple Suspense boundaries.

Suspense

Suspense(fallback: Any = None, children: Any = None) -> VNode

Show a fallback while resources read under the boundary are pending.

Parameters:

Name Type Description Default
fallback Any

VNode, string, or callable returning one of those. Shown while any registered resource is pending.

None
children Any

Children rendered when nothing is pending. Resources called anywhere in this subtree self-register with the boundary while they're in their initial "pending" state.

None

Returns:

Type Description
VNode

A component VNode that toggles between

VNode

fallback and children.

SuspenseList

SuspenseList(children: Any = None, reveal_order: str = 'forwards', tail: Optional[str] = None) -> VNode

Coordinate the reveal order of multiple Suspense boundaries.

Matches SolidJS's <SuspenseList>. Each Suspense boundary mounted underneath (that isn't nested inside another boundary) registers with the list in mount order, and the list decides when each may reveal its content and whether it shows its fallback.

Parameters:

Name Type Description Default
children Any

Children containing one or more Suspense boundaries.

None
reveal_order str

One of "forwards" (default; contents reveal top-to-bottom, each waiting for the ones before it), "backwards" (bottom-to-top), or "together" (all reveal at once when every boundary has loaded).

'forwards'
tail Optional[str]

Fallback policy for still-pending boundaries. None (default) shows every pending boundary's fallback, "collapsed" shows only the next fallback in reveal order, and "hidden" shows none.

None

Returns:

Type Description
VNode

A component VNode.

Example
SuspenseList(
    reveal_order="forwards",
    tail="collapsed",
    children=[
        Suspense(fallback=p("Loading A..."), children=[PanelA()]),
        Suspense(fallback=p("Loading B..."), children=[PanelB()]),
    ],
)
Note

A boundary whose content hasn't mounted yet doesn't start its resource fetches, so "forwards" reveals sequentially-loading content as a cascade rather than loading everything in parallel. Start fetches outside the boundaries (or pass resources down as props) when parallel loading matters.

What's in this module

Suspense renders a fallback while any create_resource in its subtree is pending. It's the canonical way to coordinate loading states for async data and lazy components.

SuspenseList coordinates multiple sibling Suspense boundaries, controlling their reveal order (reveal_order="forwards" | "backwards" | "together") and which fallbacks show while loading (tail=None | "collapsed" | "hidden").

Usage

from wybthon import Suspense, component, create_resource, create_signal
from wybthon.html import div, p, span


async def fetch_user(id_: int) -> dict:
    ...


@component
def UserCard(id):
    user = create_resource(id, fetch_user)
    return div(
        p("Name: ", span(lambda: user()["name"])),
    )


@component
def Profile():
    id_, _ = create_signal(42)
    return Suspense(
        fallback=lambda: p("Loading…"),
        children=lambda: UserCard(id=id_),
    )
  • fallback is a callable so it can stay reactive too.
  • The boundary waits for all pending resources in its subtree.

See also