Skip to content

Components

Components

Wybthon uses function components exclusively, following the SolidJS model.

The @component decorator lets you define components using Pythonic keyword arguments instead of a raw props dict:

from wybthon import component, div, h2

@component
def Hello(name: str = "world"):
    return h2(f"Hello, {name()}!", class_name="greeting")

Props become regular Python parameters with type annotations and defaults. This makes components self-documenting and enables static type checking.

Stateless components return a VNode directly and re-render when the parent passes new props:

@component
def Greeting(name: str = "world"):
    return p(f"Hello, {name()}!")

Stateful components create signals during setup and return a render function. Setup runs once; the render function re-runs when signals change:

from wybthon import button, component, create_signal, div, p

@component
def Counter(initial: int = 0):
    count, set_count = create_signal(initial())

    def render():
        return div(
            p(f"Count: {count()}"),
            button("+1", on_click=lambda e: set_count(count() + 1)),
        )
    return render

Children are received via a children parameter:

from wybthon import component, h3, section

@component
def Card(title: str = "", children=None):
    kids = children()
    kids = kids if isinstance(kids, list) else ([kids] if kids else [])
    return section(h3(title()), *kids, class_name="card")

Direct calls with keyword args return a VNode, so you can compose components without h():

Counter(initial=5)
Card("child1", "child2", title="My Card")   # positional args become children

The component still works with h() as usual:

from wybthon import h

h(Counter, {"initial": 5})

See: Primitives for the full signals-first API.

Traditional function components

You can still define components the traditional way with a props dict. This style is fully supported and does not require a decorator:

from wybthon import div, h2

def Hello(props):
    name = props.get("name", "world")
    return h2(f"Hello, {name}!", class_name="greeting")

The HTML helpers accept children as positional arguments and props as keyword arguments. Use class_name instead of class (reserved word in Python) and html_for instead of for.

Fragment

Use Fragment to group children without adding a visible wrapper element:

from wybthon import Fragment, h1, p

@component
def PageContent():
    return Fragment(
        h1("Title"),
        p("Body text here."),
    )

memo

Wrap a function component with memo to skip re-renders when its props have not changed (shallow identity comparison by default):

from wybthon import component, memo, h

@component
def ExpensiveList(items=None):
    its = items() or []
    return h("ul", {}, *[h("li", {}, str(i)) for i in its])

MemoList = memo(ExpensiveList)

Pass a custom comparison function for deeper control:

MemoList = memo(ExpensiveList, are_props_equal=lambda old, new: old["items"] == new["items"])

forward_ref

Use forward_ref to create a component that can receive a ref prop and forward it to a child element:

from wybthon import forward_ref, h

def _render(props, ref):
    return h("input", {"type": "text", "ref": ref, "class": "fancy-input"})

FancyInput = forward_ref(_render)

# Usage: h(FancyInput, {"ref": my_ref})

The wrapped function receives (props, ref) instead of (props,). When no ref is provided, ref is None.

create_portal

Use create_portal to render children into a DOM node outside the parent component's hierarchy — ideal for modals, tooltips, and overlays:

from wybthon import component, create_portal, h

@component
def Modal():
    return h("div", {},
        h("p", {}, "Page content"),
        create_portal(
            h("div", {"class": "modal"}, "I appear in #modal-root!"),
            "#modal-root",
        ),
    )

The second argument is an Element or a CSS selector string.

Flow control

Wybthon provides SolidJS-style flow control components for declarative rendering logic:

from wybthon import Show, For, Switch, Match

# Conditional rendering
Show(is_logged_in(), p("Welcome!"), fallback=p("Please log in"))

# List rendering (keyed)
For(items(), lambda item, idx: li(item, key=idx()))

# Multi-branch matching
Switch(
    Match(status() == "loading", p("Loading...")),
    Match(status() == "error", p("Error!")),
    Match(status() == "ready", p("Ready")),
    fallback=p("Unknown"),
)

See the guide for recommended patterns around props, state, children, cleanup, and context: