Migrating from Solid¶
Wybthon is essentially SolidJS for Python. Most of the primitives have direct equivalents and the mental model is identical: components run once, signals drive fine-grained updates, and the ownership tree manages cleanup.
The differences are mostly cosmetic: Python instead of JavaScript, builder functions instead of JSX, and a few naming conventions to keep things idiomatic.
API mapping¶
| SolidJS | Wybthon |
|---|---|
createSignal(initial) |
create_signal(initial) |
createEffect(fn) |
create_effect(fn) |
createMemo(fn) |
create_memo(fn) |
createResource(source, fetcher) |
create_resource(source, fetcher) |
createContext(default) / useContext |
create_context / use_context |
<Show when={...} fallback={...}> |
Show(when=..., fallback=...) |
<For each={...}> |
For(each=..., children=...) |
<Index each={...}> |
Index(each=..., children=...) |
<Switch> / <Match> |
Switch / Match |
<Dynamic component={...} /> |
Dynamic(component=...) |
<Portal mount={...}> |
create_portal(mount=...) |
<ErrorBoundary fallback={...}> |
ErrorBoundary(fallback=...) |
<Suspense fallback={...}> |
Suspense(fallback=...) |
lazy(() => import(...)) |
lazy(load=...) |
onMount(fn) |
on_mount(fn) |
onCleanup(fn) |
on_cleanup(fn) |
batch(fn) |
batch(fn) |
untrack(fn) |
untrack(fn) |
on(deps, fn) |
on(deps, fn) |
createStore(initial) |
create_store(initial) |
produce(fn) |
produce(fn) |
Templates¶
Solid uses JSX. Wybthon uses Python builders from wybthon.html:
from wybthon import component
from wybthon.html import p
@component
def Greeting(name):
return p("Hello, ", name, "!")
Tag helpers are defined for every standard HTML element. For custom elements, use h directly.
Props¶
Solid props are reactive getters on a proxy object. Wybthon props arrive as callables:
You can pass title straight through (creating a reactive hole) or read title() inside an effect. Destructuring (assigning the value to a local) freezes it at mount, just like Solid.
For ergonomic prop manipulation Wybthon offers get_props (analogous to Solid's splitProps):
from wybthon import get_props
@component
def Button(label, **rest):
props, others = get_props(rest, ["disabled"])
return button(label, disabled=props["disabled"], **others)
Signals and effects¶
Identical in spirit and behavior:
create_effect re-runs whenever signals it tracked during the previous run change. There is no manual dep array.
Stores¶
from wybthon import create_store, produce
state, set_state = create_store({"count": 0, "items": []})
# atomic update:
with produce(state) as draft:
draft["count"] += 1
draft["items"].append("new")
Stores wrap nested data in lazy proxies so reads are tracked at the leaf level, exactly like Solid.
Routing¶
from wybthon import Route, Router, Link
routes = [
Route(path="/", component=Home),
Route(path="/users/:id", component=User),
]
@component
def App():
return Router(routes=routes)
Wybthon's router supports nested routes, dynamic params, query parsing, and lazy components; see Routing.
What's intentionally different¶
- Naming. snake_case across the API (
create_signal, notcreateSignal). Component names stay PascalCase. - Imports. Pull from
wybthon(and optionallywybthon.htmlfor tag helpers). Dynamic. Usedynamic(lambda: ...)to inline a reactive computation; component-styleDynamicexists too.- JS interop. Use
pyodide.ffito talk to the host. See Pyodide guide.
What carries over directly¶
- The mental model (components run once, fine-grained reactivity).
- Ownership semantics:
on_cleanupattaches to the current owner. - Transitions and resources:
create_resourceintegrates withSuspense. - Patterns like keyed lists, conditional flows, and nested boundaries.
Next steps¶
- Read Mental model for the framework's core ideas.
- Explore Authoring patterns; many should look familiar.
- Browse the API reference for the full set of primitives.