Flow control
wybthon.flow¶
flow
¶
Control flow: Show, For, Repeat, Switch/Match, dynamic, and client_only.
These primitives create isolated reactive scopes so only the
relevant subtree updates when a condition or list changes. Each is a
function returning a component VNode; conditions and sources are
accessors (or plain values), and children/fallback slots are VNodes
or callables evaluated inside the primitive's own scope.
Callback shapes follow SolidJS 2.0:
Show(when, children): a callablechildrenreceives anAccessorfor the truthy value (or the value itself withkeyed=True).For(each, children, keyed=True):children(item, index)where the shapes depend onkeyed(seeFor).Repeat(count, children):children(index: int).Switch(Match(when, children), ..., fallback=...).dynamic(source): a component whose implementation is chosen reactively.client_only(children, fallback=...): content that only renders in the browser, after hydration.
Example
Classes:
| Name | Description |
|---|---|
Match |
A branch of a |
Functions:
| Name | Description |
|---|---|
Show |
Render |
For |
Render a list with a stable subtree per row. |
Repeat |
Render |
Switch |
Render the first |
dynamic |
Turn an accessor for a component (or tag) into a component you can call. |
client_only |
Render |
is_hydrating |
Whether the browser is adopting server-rendered DOM right now. |
NoHydration |
Render |
Hydration |
Mark a subtree for hydration; a passthrough in Wybthon. |
Match
¶
Show
¶
Render children while when is truthy, else fallback.
Only the truthiness of when is tracked by default: the branch
re-renders when the condition flips, not on every value change. A
callable children may accept one argument, an
Accessor for the (truthy) value, so inner
holes can read it reactively.
With keyed=True, children re-renders whenever the value itself
changes and receives the raw value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
when
|
Any
|
Condition accessor or plain value. |
required |
children
|
Any
|
VNode, zero-arg callable, or |
None
|
fallback
|
Any
|
Rendered when |
None
|
keyed
|
bool
|
Re-create the branch on every value change. |
False
|
For
¶
For(each: Any, children: Callable[..., Any], fallback: Any = None, *, keyed: bool | Callable[[Any], Any] = True) -> VNode
Render a list with a stable subtree per row.
The mapping callback runs once per row; when the list changes, existing rows keep their DOM and are only moved, never re-diffed. A row's reactive scope is disposed when it leaves the list.
keyed selects how rows are matched, and with it the callback
shape:
True(default): match by identity (scalars by value).children(item, index)receives the raw item and anAccessor[int]index.False: match by position.children(item, index)receives anAccessorfor the item at that position and anintindex.- a callable
key(item): match by key, updating the row in place when a new object has the same key.children(item, index)receives accessors for both.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
each
|
Any
|
List accessor (or a plain list, which renders once). |
required |
children
|
Callable[..., Any]
|
The row callback. |
required |
fallback
|
Any
|
Rendered when the list is empty. |
None
|
keyed
|
bool | Callable[[Any], Any]
|
Matching strategy. |
True
|
Repeat
¶
Repeat(count: Any, children: Callable[[int], Any], fallback: Any = None, *, start: int | Any = 0) -> VNode
Render children(i) for i in range(start, start + count) with no diffing.
Rendering is driven purely by the count: growing mounts new tail slots, shrinking disposes them, and nothing else is touched. Use it for pagination dots, ratings, skeletons, and other count-driven UI.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
count
|
Any
|
Count accessor or plain integer. |
required |
children
|
Callable[[int], Any]
|
|
required |
fallback
|
Any
|
Rendered when the count is zero. |
None
|
start
|
int | Any
|
First index (accessor or int). |
0
|
Switch
¶
Render the first Match whose condition is truthy.
Conditions are evaluated in order inside the switch's own scope;
only a change in which branch matches re-renders, so unrelated
value changes are ignored (unless a branch is keyed).
dynamic
¶
Turn an accessor for a component (or tag) into a component you can call.
The returned callable behaves like any component: call it with
children and props to get a VNode. Each instance re-mounts when
source resolves to a different component; while source is an
async computation with no value yet, the instance keeps its current
content and the nearest Loading shows its
fallback. Passing None renders nothing.
This is the counterpart of SolidJS 2.0's dynamic(). For a one-off
use, call the result inline: dynamic(lambda: views[kind()])(title="Hi").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Any
|
An accessor returning a component, a tag name, or
|
required |
client_only
¶
Render children only in the browser, after hydration.
During a server render, and while the browser hydrates that render,
fallback shows instead; the children replace it as soon as
hydration has committed. In a page that wasn't server-rendered,
the children render immediately. Use it for widgets that need
browser APIs or whose output depends on the browser (a map, a chart
measured from the viewport, the user's local time).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
children
|
Any
|
A VNode or a zero-arg callable returning the content. |
required |
fallback
|
Any
|
What the server renders, and what shows until hydration finishes. |
None
|
is_hydrating
¶
is_hydrating() -> bool
Whether the browser is adopting server-rendered DOM right now.
True only during the synchronous mount of hydrate:
render what the server rendered, then switch to client-only values
from on_settled. Always false on the server and in pages that
weren't server-rendered.
NoHydration
¶
Render children on the server as static HTML the browser won't hydrate.
While the browser hydrates, the region's server-rendered DOM is kept as is and nothing inside it is mounted, so it costs no Python work and never updates. In a page that wasn't server-rendered, the children render normally. Use it for content that never changes after the first paint: article bodies, footers, legal text.
Matches Solid 2.0's <NoHydration>.
Hydration
¶
Mark a subtree for hydration; a passthrough in Wybthon.
Solid 2.0 uses <Hydration> to re-enable hydration for an island
inside <NoHydration>. Wybthon keeps a NoHydration region static
as a whole while hydrating, so this renders its children unchanged.
It's provided so code shared with Solid's model reads the same.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*children
|
Any
|
The content. |
()
|
id
|
str | None
|
Accepted for parity; unused. |
None
|
What's in this module¶
Control-flow primitives that create isolated reactive scopes, so only
the relevant subtree updates when a condition or list changes. Each is a
function returning a component VNode; conditions and sources are
accessors (or plain values), and children and fallback slots are
VNodes or callables evaluated inside the primitive's own scope. All
arguments are positional except the keyword-only options noted below.
| Name | Description |
|---|---|
Show |
Show(when, children, fallback=None, *, keyed=False): render one branch by truthiness. |
For |
For(each, children, fallback=None, *, keyed=True): one cached row per item; rows move, never re-diff. |
Repeat |
Repeat(count, children, fallback=None, *, start=0): children(i) for i in range(start, start + count). |
Switch / Match |
Switch(Match(when, children, keyed=False), ..., fallback=None): first truthy branch wins. |
dynamic |
dynamic(source): a component whose implementation (a component or tag) is chosen by a reactive source. Call the result with children and props. |
client_only |
client_only(children, *, fallback=None): render children only in the browser, after hydration; fallback renders on the server. |
NoHydration |
NoHydration(*children): server-rendered HTML the browser keeps as static DOM instead of hydrating. |
Hydration |
Hydration(*children, id=None): a passthrough kept for parity with Solid 2.0. |
is_hydrating |
True only during the synchronous mount of hydrate. |
For keying shapes¶
keyed selects how rows are matched between updates, and with it the
callback shape:
keyed |
Rows match by | children(item, index) receives |
|---|---|---|
True (default) |
Identity (scalars by value) | the raw item, Accessor[int] |
False |
Position | Accessor[T], int |
key(item) callable |
The key; a new object with the same key updates the row in place | Accessor[T], Accessor[int] |
The row callback runs once per row inside the row's owner scope and
untracked; anything reactive inside a row must read an accessor within
a hole, memo, or effect. Passing a plain list for each renders once
and warns in dev mode.
from wybthon import For, Match, Repeat, Show, Switch, create_signal, li, p, span, ul
todos, set_todos = create_signal([{"id": 1, "title": "Ship", "done": False}])
status, set_status = create_signal("ready")
rating, set_rating = create_signal(3)
user, set_user = create_signal(None)
view = ul(
Show(user, lambda u: li("Hello, ", lambda: u()["name"]), fallback=li("Sign in")),
For(todos, lambda todo, i: li(lambda: f"{i() + 1}. {todo['title']}")),
For(todos, lambda todo, i: li(lambda: todo()["title"]), keyed=lambda t: t["id"]),
li(Repeat(rating, lambda i: span("*"), start=1)),
Switch(
Match(lambda: status() == "loading", lambda: p("Loading...")),
Match(lambda: status() == "ready", lambda: p("Ready")),
fallback=lambda: p("Unknown"),
),
)
Showtracks only the truthiness ofwhen; a callablechildrenmay take the value accessor (or the raw value withkeyed=True, which re-creates the branch on every change).Repeatis driven purely by the count: growing mounts tail slots, shrinking disposes them, and nothing else is touched.countandstartmay be accessors or ints.dynamicaccepts a tag name, a component,None, or an accessor returning one of those; each instance remounts when the resolved component changes.client_onlyrenders its fallback during a server render and while hydrating, then its children; see Server rendering.NoHydrationcosts no Python work while hydrating: the kernel keeps the region's server DOM as is (CLAIM_STATIC) and nothing inside it mounts or updates. In a page that wasn't server-rendered, its children render normally.
See also¶
map_arrayandrepeat: the reactive mapping behindForandRepeatcreate_projection: per-row selection that notifies only the changed rows- Concepts: Components
- Guides: Authoring patterns
- Guides: Performance