VNode
wybthon.vnode¶
vnode
¶
Virtual node data structure and tree-building helpers.
This module defines VNode and the functions that
build trees of them: h, Fragment,
and hole. It has no browser or DOM dependencies, so
VNode trees can be constructed and inspected anywhere CPython runs.
A reactive hole is a _hole VNode wrapping a reactive expression
(an Accessor or a zero-argument function). The
reconciler runs the expression inside its own render effect and patches
only that region of the DOM when its dependencies change. Holes are
created implicitly whenever a reactive expression appears in a child
position, and explicitly with hole() when you want a key.
Example
Building a small subtree without a browser:
Classes:
| Name | Description |
|---|---|
VNode |
Virtual node representing an element, text, component, fragment, or hole. |
Functions:
| Name | Description |
|---|---|
hole |
Create a reactive hole explicitly, optionally with a |
render_template |
Render a t-string to text, calling reactive interpolations when |
template_getter |
Return a reactive expression rendering |
h |
Create a VNode from a tag, props, and children. |
Fragment |
Group children without adding a wrapper element. |
copy_vnode |
Return a structural copy of an unmounted VNode tree (other values unchanged). |
VNode
¶
VNode(tag: str | Callable[..., Any] | None, props: PropsDict | None = None, children: list[Any] | None = None, key: str | int | None = None)
Virtual node representing an element, text, component, fragment, or hole.
Uses __slots__ for a compact layout. The renderer populates the
internal attributes (el, subtree, render_effect,
component_ctx, ns, _frag_end) when the VNode mounts.
Attributes:
| Name | Type | Description |
|---|---|---|
tag |
Element tag name ( |
|
props |
PropsDict
|
Mapping of prop names to values: attributes, event handlers, refs, and reactive bindings. |
children |
list[Any]
|
Child VNodes (mixed values before normalization). |
key |
Optional stable identity for keyed reconciliation. |
|
el |
int | None
|
Kernel node id once mounted (for fragments the start marker; for holes the end marker). |
owner_scope |
Any
|
Optional reactive |
to_text_vnode
¶
Convert an arbitrary value to a text VNode (None becomes "").
hole
¶
Create a reactive hole explicitly, optionally with a key.
Reactive expressions in child positions become holes automatically;
reach for hole() when you need a stable key for keyed
reconciliation inside a fragment, or to make a hole visually
explicit.
The expression may return a VNode, a string, a list of either, or
None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
getter
|
Callable[[], Any]
|
Zero-arg reactive expression evaluated inside its own render effect. |
required |
key
|
str | int | None
|
Optional stable identity for keyed reconciliation. |
None
|
Returns:
| Type | Description |
|---|---|
VNode
|
A |
render_template
¶
Render a t-string to text, calling reactive interpolations when call is true.
template_getter
¶
Return a reactive expression rendering template, or None when it's static.
A t-string is reactive when at least one interpolation is an accessor or zero-argument function. The expression calls every reactive interpolation, so the whole string updates as one binding.
flatten_children
¶
Flatten nested child lists into a single list, dropping None entries.
normalize_children
¶
Normalize a mixed list of children into a flat list of VNodes.
VNode: kept as is; fragments are flattened into the parent list.- Reactive expression (accessor or zero-arg function): wrapped in a
_holeVNode. None,True, andFalse: skipped, socond and Widget()renders nothing when the condition is false (as in SolidJS).- Anything else: coerced to a text VNode.
h
¶
Create a VNode from a tag, props, and children.
The low-level constructor behind the helpers in
wybthon.html. Reactive expressions in child
positions are wrapped as holes when the parent mounts; components
receive their children verbatim through the children prop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tag
|
str | Callable[..., Any] | None
|
An HTML tag name, a special tag ( |
required |
props
|
PropsDict | None
|
Mapping of prop names to values. May be |
None
|
*children
|
Any
|
Children to attach. Lists and tuples are flattened. |
()
|
Returns:
| Type | Description |
|---|---|
VNode
|
A new |
Fragment
¶
Group children without adding a wrapper element.
Fragments mount their children directly into the parent between two
empty comment markers, so they never pollute selectors like
:first-child or affect layout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Children. Lists and tuples are flattened. |
()
|
Returns:
| Type | Description |
|---|---|
VNode
|
A |
What's in this module¶
vnode defines the VNode data structure and the pure
helpers that build trees of them. It has no browser dependency, so
trees can be constructed and inspected anywhere CPython runs. A
reactive hole is a _hole VNode wrapping an accessor or zero-arg
function; the reconciler runs it in its own render effect and patches
only that region when its reads change.
| Name | Description |
|---|---|
VNode |
Element, text, component, fragment, or hole node; tag, props, children, key. |
h |
Hyperscript constructor: h(tag, props, *children); components get children as the children prop. |
Fragment |
Group children with no wrapper element. |
hole |
Explicit reactive hole, optionally with a key. |
Holes are created implicitly: any accessor or zero-arg callable in a
child position becomes one, and so does a template string that
interpolates one (t"Hello, {name}", a single hole for the whole
string). Reach for hole() when you need a stable key or want the
hole visually explicit.
VNode[...] sets children with item syntax and returns the node, so
section(class_="card")[h1("Hello"), p("Body")] equals
section(h1("Hello"), p("Body"), class_="card"). A component call
supports the same syntax for its children prop.
A subtree mounted from a compiled shape keeps its VNodes, but only the
root and its dynamic children get el assigned at mount; static
descendants receive node ids lazily, when the reconciler first patches
the subtree. Don't rely on .el of a static descendant.
from wybthon import Fragment, create_signal, h, hole
name, set_name = create_signal("Ada")
view = h(
"section",
{"class": "card"},
h("h1", {}, "Hello, ", name), # implicit hole
hole(lambda: f"{len(name())} letters", key="count"), # explicit hole with a key
h("p", {}, t"Name: {name}"), # t-string hole
Fragment(h("p", {}, "Body 1"), h("p", {}, "Body 2")),
)
The wybthon.html helpers wrap h() with keyword props, so
most application code writes section(h1("Hello, ", name), class_="card")
instead.
See also¶
- HTML helpers and SVG helpers
is_accessor: the rule that decides what becomes a hole- Reconciler: how VNodes mount and patch
- Concepts: Virtual DOM