Skip to content

Flow control

wybthon.flow

flow

SolidJS-style reactive flow control components.

These components create isolated reactive scopes so that only the relevant subtree re-renders when the tracked condition or list changes.

Each flow control is a factory function returning a component VNode; conditions, sources, children, and fallbacks are accepted as getters (zero-arg callables) so that reads happen inside the flow control's own reactive effect, not the parent's.

API rules:

  • when / each: pass a getter (the signal accessor itself) or a raw value. Getters are called inside the flow control's own scope.
  • children: may be a VNode, a callable returning a VNode, or (for For / Index) the per-item mapping callback.
  • fallback: same flexibility as children.

Fine-grained list primitives:

  • For maintains stable per-item rendered subtrees (keyed by reference identity) on top of map_array. The mapping callback runs exactly once per unique item; on list changes, existing rows keep their DOM and are only moved, never re-diffed.
  • Index maintains stable per-index subtrees on top of index_array, with a reactive item signal that updates when the value at that position changes.
Example
Show(when=is_logged_in,
     children=lambda: p("Welcome!"),
     fallback=lambda: p("Please log in"))

For(each=items,
    children=lambda item, idx: li(item()))

Functions:

Name Description
Show

Conditionally render children when when is truthy.

For

Render a list of items using a per-item mapping function.

Index

Render a list by index with a stable item getter.

Match

Declare a branch inside a Switch.

Switch

Render the first matching Match branch, or fallback.

Dynamic

Render a dynamically-chosen component.

Show

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

Conditionally render children when when is truthy.

Show(when=count, children=lambda: p("Count: ", count),
     fallback=lambda: p("Empty"))

Behavior:

  • when may be a zero-arg getter or a plain value.
  • children / fallback may be a VNode, a callable, or a plain value. When children is callable and when is truthy, the truthy value is passed as the first argument (matching SolidJS <Show>).

The component creates a keyed conditional scope: when the truthiness of when changes, the previous branch's scope is disposed and a new scope is created. This ensures that effects and cleanups registered inside a branch are properly torn down on transitions.

Parameters:

Name Type Description Default
when Any

Condition value or zero-arg getter.

None
children Any

Slot rendered when the condition is truthy.

None
fallback Any

Slot rendered when the condition is falsy.

None

Returns:

Type Description
VNode

A component VNode that re-renders when the

VNode

condition's truthiness changes.

For

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

Render a list of items using a per-item mapping function.

For(each=items,
    children=lambda item, index: li(item()))

Inside the callback, item is a signal-backed getter returning the current item value, and index is a signal-backed getter returning the current integer index, matching SolidJS <For>.

For is built on map_array: the mapping callback runs exactly once per unique item (keyed by reference identity), and the rendered subtree is cached. When the list changes, unchanged rows keep their DOM untouched; the reconciler only mounts additions, unmounts removals, and moves reordered rows. When an item leaves the list, its reactive scope (including any effects or cleanups created inside the callback) is disposed.

Parameters:

Name Type Description Default
each Any

List getter (typically a signal accessor) or plain list.

None
children Any

A (item_getter, index_getter) -> VNode callable.

None
fallback Any

Slot rendered when the list is empty.

None

Returns:

Type Description
VNode

A component VNode.

Index

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

Render a list by index with a stable item getter.

Unlike For, the children callback receives (item_getter, index) so that the DOM subtree for each index is reused even when the underlying data changes.

Index is built on index_array: each slot renders once and owns a signal-backed item_getter that updates when the value at that position changes. Growing the list creates and mounts new slots; shrinking disposes and unmounts excess slots.

Parameters:

Name Type Description Default
each Any

List getter (typically a signal accessor) or plain list.

None
children Any

A (item_getter, index: int) -> VNode callable.

None
fallback Any

Slot rendered when the list is empty.

None

Returns:

Type Description
VNode

A component VNode.

Match

Match(when: Any = None, children: Any = None) -> _MatchResult

Declare a branch inside a Switch.

when may be a getter or a plain value:

Match(when=lambda: x() > 0, children=lambda: p("positive"))

Must be used inside Switch().

Parameters:

Name Type Description Default
when Any

Predicate value or zero-arg getter.

None
children Any

A VNode, a callable returning a VNode, or a plain value to coerce to text.

None

Returns:

Type Description
_MatchResult

An opaque branch descriptor consumed by Switch.

Switch

Switch(*branches: _MatchResult, fallback: Any = None) -> VNode

Render the first matching Match branch, or fallback.

Switch(
    Match(when=lambda: status() == "loading",
          children=lambda: p("Loading...")),
    Match(when=lambda: status() == "ready",
          children=lambda: p("Ready")),
    fallback=lambda: p("Unknown"),
)

Each Match when is evaluated lazily inside the Switch component's reactive scope.

Parameters:

Name Type Description Default
*branches _MatchResult

One or more Match results, in priority order.

()
fallback Any

Slot to render when no branch matches. May be a VNode, a callable, or a plain value.

None

Returns:

Type Description
VNode

A component VNode for the first matching

VNode

branch, or the fallback slot.

Dynamic

Dynamic(component: Any = None, props: Optional[Dict[str, Any]] = None, **kwargs: Any) -> VNode

Render a dynamically-chosen component.

component may be a string tag name, a component function, or None (renders nothing). It can also be a getter for reactive switching.

Parameters:

Name Type Description Default
component Any

Tag name, component callable, getter, or None.

None
props Optional[Dict[str, Any]]

Optional dict of props forwarded to the resolved component.

None
**kwargs Any

Additional props (merged on top of props).

{}

Returns:

Type Description
VNode

A component VNode that re-mounts whenever

VNode

the resolved component identity changes.

Example
Dynamic(component=lambda: heading_level(),
        children=[f"Section {idx}"])

What's in this module

flow provides SolidJS-style reactive flow control components. They create isolated reactive scopes so that only the relevant subtree re-renders when the tracked condition or list changes.

Component Use it for
Show Conditional rendering with a single fallback.
For Keyed list rendering with stable per-item scopes.
Index Index-keyed list rendering with reactive item signals.
Switch / Match Multi-branch conditional rendering.
Dynamic Render a component chosen at runtime.

Idioms

from wybthon import (
    For, Index, Match, Show, Switch, component, create_signal,
)
from wybthon.html import li, p, ul


@component
def Demo():
    items, _ = create_signal(["a", "b", "c"])
    is_logged_in, _ = create_signal(False)

    return ul(
        Show(
            when=is_logged_in,
            children=lambda: li("Welcome!"),
            fallback=lambda: li("Please log in"),
        ),
        For(each=items, children=lambda item, idx: li(item())),
    )
  • Pass getters (the signal accessor itself) to when / each.
  • children may be a VNode, a callable returning a VNode, or the per-item mapping callback for For / Index.
  • The mapping callback runs once per unique item (For) or per index slot (Index) and the resulting subtree is cached: list changes mount added items, dispose removed ones, and move existing DOM for reorders. Inside Index, pass the item getter (not item()) where the slot's value should stay live.

See also