vdom
wybthon.vdom¶
Virtual DOM primitives, diffing, and rendering to real DOM elements.
This module re-exports the public API from the focused sub-modules:
- :mod:
wybthon.vnode--VNode,h,Fragment,memo - :mod:
wybthon.reconciler--render,mount,unmount,patch - :mod:
wybthon.error_boundary--ErrorBoundary - :mod:
wybthon.suspense--Suspense - :mod:
wybthon.portal--create_portal - :mod:
wybthon.props-- prop-diffing helpers (is_event_prop, etc.)
All names previously importable from wybthon.vdom continue to work.
VNode
dataclass
¶
Virtual node representing an element, text, or component subtree.
ErrorBoundary(props)
¶
Catch render errors in children and display a fallback.
Props
- fallback: VNode | str | callable(error, reset) -> VNode
- on_error: optional callback invoked with the caught exception
- reset_key / reset_keys: when this value changes the error is auto-cleared
- children: child VNodes to render when there is no error
Fragment(*args)
¶
Group multiple children without adding a visible wrapper to the DOM.
Uses a <span style="display:contents"> so the wrapper is invisible to
CSS layout while keeping the VDOM diffing algorithm simple.
Can be called directly::
Fragment(child1, child2)
Or used as a component tag via h()::
h(Fragment, {}, child1, child2)
Suspense(props)
¶
Render a fallback while one or more resources are loading.
Props
- resources | resource: Resource or list of Resources
- fallback: VNode | str | callable returning VNode/str
- keep_previous: bool (default False)
- children: child VNodes to render when not loading
create_portal(children, container)
¶
Render children into a different DOM container.
Returns a VNode that, when mounted, renders children into container instead of the parent component's DOM node. Useful for modals, tooltips, and overlays that need to break out of their parent's DOM hierarchy.
container may be an Element or a CSS selector string.
h(tag, props=None, *children)
¶
Create a VNode from a tag, props, and children (component-aware).
is_event_prop(name)
¶
Return True if a prop name is an event handler prop like on_click or onClick.
memo(component, are_props_equal=None)
¶
Memoize a function component to skip re-renders when props are unchanged.
By default uses shallow identity comparison (is) on each prop value.
Pass a custom are_props_equal(old_props, new_props) -> bool for
deeper comparison logic.
mount(vnode, container, anchor=None)
¶
Mount a VNode (or string) into the container, returning its element.
patch(old, new, container)
¶
Patch old into new by mutating DOM as needed within the container.
render(vnode, container)
¶
Render a VNode tree into a container Element or CSS selector.
unmount(vnode)
¶
Unmount a VNode and dispose associated resources and effects.
The VDOM system is implemented as a set of focused sub-modules. The
wybthon.vdom module re-exports all public names for convenience:
| Module | Responsibility |
|---|---|
wybthon.vnode |
VNode, h(), Fragment, memo() |
wybthon.reconciler |
render(), mount(), unmount(), patch() |
wybthon.props |
DOM prop application, style/event/dataset diffing |
wybthon.error_boundary |
ErrorBoundary component |
wybthon.suspense |
Suspense component |
wybthon.portal |
create_portal() |
You can import from either wybthon.vdom or the specific sub-module:
from wybthon.vdom import h, render # re-export hub
from wybthon.vnode import h # direct import
from wybthon.reconciler import render # direct import
Public API¶
VNodeh(tag, props=None, *children) -> VNoderender(vnode, container) -> ElementErrorBoundarycomponentSuspensecomponentmemo(component, are_props_equal=None)— memoize a function componentcreate_portal(children, container)— render children into a different DOM container
Keyed children and diffing¶
h(tag, {"key": key}, ...)assigns a stable identity to a child.- During reconciliation, children are matched by key first, then by type for unkeyed nodes.
- Reorders are applied with minimal DOM moves using a right-to-left pass with a moving anchor.
- Unmatched old nodes are unmounted; unmatched new nodes are mounted at the correct anchor.
Text nodes (fast-path)¶
- When both the old and new nodes are text, the same DOM text node is reused and only
nodeValueis updated. - Lists of plain text children are reconciled efficiently; typically the framework updates text in-place and minimizes DOM moves.
- Example:
from wybthon import h, render
from wybthon.dom import Element
root = Element(node=document.createElement("div"))
render(h("div", {}, "hello"), root)
render(h("div", {}, "world"), root) # updates the same text node
Suspense¶
Suspense renders a fallback while one or more resources are loading.
- Props:
resourceorresources=[...]fallback– VNode/str/callablekeep_previous=False– keep children visible during subsequent reloads
ErrorBoundary¶
ErrorBoundary catches render errors from its subtree and renders a fallback.
- Props:
fallback– VNode/str/callable. When callable, it is invoked asfallback(error, reset); if the callable only accepts one argument, it is invoked asfallback(error).on_error– optional callback called with the thrown error when the boundary captures it.reset_key– any value; when this value changes, the boundary automatically resets (clears the error) on the next render.-
reset_keys– list/tuple of values; when the tuple of values changes, the boundary automatically resets. -
Methods:
-
reset()– imperative method to clear the current error and attempt re-rendering children. -
Notes:
- If the fallback callable throws, a simple text node "Error rendering fallback" is shown.
- When not in an error state, the boundary renders its
childrenwrapped in aFragment.
Prop semantics (style, dataset, value, checked)¶
style: pass a dict of CSS properties using camelCase keys. Keys are converted to kebab-case and applied viastyle.setProperty. On updates, keys that are absent in the new dict are removed withstyle.removeProperty. PassingNoneor a non-dict clears previously set style keys.
python
h("div", {"style": {"backgroundColor": "red", "fontSize": 14}})
# → sets background-color: red; font-size: 14
dataset: pass a dict; entries map todata-*attributes. On updates, keys not present are removed. PassingNoneor a non-dict clears previously setdata-*attributes.
python
h("div", {"dataset": {"id": "x", "role": "button"}})
# → sets data-id="x" data-role="button"
-
value: for form controls, the DOMvalueproperty is set (falling back to thevalueattribute if needed).Nonebecomes "". Removing thevalueprop resets it to "". -
checked: for checkboxes/radios, the DOMcheckedproperty is set when available (falling back to thecheckedattribute). Removing thecheckedprop clears it toFalse.
memo¶
memo(component, are_props_equal=None) wraps a function component to skip re-renders when props are unchanged.
- By default, uses shallow identity comparison (
is) on each prop value. - Pass a custom
are_props_equal(old_props, new_props) -> boolfor deeper comparison.
from wybthon import memo
def ExpensiveList(props):
# ... render a large list ...
pass
MemoList = memo(ExpensiveList)
create_portal¶
create_portal(children, container) renders children into a different DOM container.
- children: a single VNode or a list of VNodes.
- container: an
Elementor CSS selector string.
from wybthon import create_portal, h
portal = create_portal(h("div", {}, "Modal content"), "#modal-root")
Development mode¶
Wybthon includes a development mode (DEV_MODE = True by default) that provides
clear error messages to stderr when something goes wrong during rendering, event
handling, or lifecycle hooks. Errors include component names and tracebacks.
from wybthon import set_dev_mode
set_dev_mode(False) # disable for production