Demo App
Demo App¶
The demo is served from examples/demo/.
index.htmlloadsbootstrap.jsbootstrap.jsloads Pyodide, mounts the library fromsrc/wybthon/, and copies demo files under/appinside Pyodide FS, then callsapp.main.main()
Folders under examples/demo/app/ mirror routes and components.
Routing and lazy loading¶
Routes are defined in examples/demo/app/routes.py. Components are
passed directly; the new @component decorator handles the
(props,) calling convention used by the router:
from app.errors.page import Page as ErrorsPage
from app.fetch.page import FetchPage
from app.page import Page as HomePage
from wybthon import Route, lazy, load_component
def _AboutLazy():
return ("app.about.page", "Page")
def _TeamLazy():
return ("app.about.team.page", "Page")
# Eager dynamic loader (resolves at route creation time).
Docs = load_component("app.docs.page", "Page")
def create_routes():
return [
Route(path="/", component=HomePage),
Route(
path="/about",
component=lazy(_AboutLazy),
children=[
Route(path="team", component=lazy(_TeamLazy)),
],
),
Route(path="/fetch", component=FetchPage),
Route(path="/errors", component=ErrorsPage),
Route(path="/docs/*", component=Docs),
]
The demo also preloads the Team route on hover for snappier transitions:
from wybthon import Link, component, h, nav, preload_component, untrack
@component
def Nav(base_path=None):
bp = untrack(base_path)
lp = {"base_path": bp, "class_": "nav-link", "class_active": "active"}
def preload_team(_evt):
try:
preload_component("app.about.team.page", "Page")
except Exception:
pass
return nav(
h(Link, {**lp, "to": "/"}, "Home"),
h(Link, {**lp, "to": "/about"}, "About"),
h(Link, {**lp, "to": "/about/team", "on_mouseover": preload_team}, "Team"),
h(Link, {**lp, "to": "/fetch"}, "Fetch"),
h(Link, {**lp, "to": "/docs"}, "Docs"),
class_="app-nav",
)
Suspense for loading UI¶
The Fetch page uses Suspense to show a fallback while its
create_resource is loading, and keeps the previous content visible
on reloads:
from wybthon import Suspense, component, create_resource, dynamic, h, p
@component
def FetchPage():
res = create_resource(fetcher)
def display_text():
err = res.error.get()
if err:
return str(err)
return res.data.get() or "No data"
return h(Suspense, {
"resource": res,
"fallback": p("Loading..."),
"keep_previous": True,
"children": [p(dynamic(display_text))],
})
This mirrors how you'd code-split larger apps and warm the import cache based on intent.
Next steps¶
- Explore the Examples for individual feature walkthroughs.
- Read Suspense and Lazy Loading.
- See the Dev server guide for the local feedback loop.