Router
wybthon.router¶
router
¶
Client-side router components and navigation helpers for Pyodide apps.
Import it from wybthon.router:
Route: declarative mapping of a path pattern to a component, optionally with nested children.Router: component that renders the matched route's component and provides the route context.Link: anchor element that navigates via the History API and toggles an active class.navigate: programmatic navigation helper.current_path: accessor for the current pathname plus query string.use_params/use_query: accessors for the matched route's params and the parsed query string.
The matched component receives params and query as reactive props
(declare them by subclassing RouteProps),
so navigating between /users/1 and /users/2 updates the mounted
component's props instead of remounting it. Path matching
(resolve) has no browser dependency, so it
also runs in tests, tools, and on the server.
Example
See Also
Classes:
| Name | Description |
|---|---|
RouteSpec |
Minimal route spec for resolving paths without components (tests and tools). |
QueryParams |
Decoded query parameters with last-value lookup and |
RouteProps |
Props for a routed component: the matched path params and the query string. |
Route |
Declarative route definition mapping a path to a component. |
Functions:
| Name | Description |
|---|---|
resolve |
Resolve a pathname to the best matching route and params. |
navigate |
Navigate within this origin, resolving relative URLs and preserving hashes. |
use_params |
Accessor for the matched route's params ( |
use_query |
Accessor for the parsed query string ( |
use_base_path |
The base path of the surrounding |
Router |
Render the component of the route matching |
Outlet |
Render the next matched child route inside a persistent parent layout. |
use_hash |
Read the decoded current URL fragment, without its leading hash. |
preload |
Warm route code and data through the surrounding router. |
Link |
An anchor that navigates with the History API and marks itself active. |
Attributes:
| Name | Type | Description |
|---|---|---|
current_path |
Accessor[str]
|
Accessor for the current pathname plus query string. |
current_path
module-attribute
¶
Accessor for the current pathname plus query string.
Updated by navigate and by the global popstate
listener (back/forward navigation). Read it inside reactive scopes to
re-render when the URL changes. During a server render it returns the
URL passed to the render function.
RouteContext
module-attribute
¶
RouteContext: Context[_RouteState | None] = create_context(None, name='Route')
Context the router provides to the matched subtree (params, query, base path).
RouteSpec
dataclass
¶
RouteProps
¶
RouteProps(**values: Any)
Bases: Props
Props for a routed component: the matched path params and the query string.
Subclass it to add your own fields. Both update in place when the URL changes but the same route still matches.
Route
dataclass
¶
Route(path: str, component: Any, children: list[Route] = list(), preload: Callable[[dict[str, Any]], Any] | None = None)
Declarative route definition mapping a path to a component.
Attributes:
| Name | Type | Description |
|---|---|---|
path |
str
|
Route pattern (e.g., |
component |
Any
|
The component to render when the path matches. It
receives |
children |
list[Route]
|
Optional nested routes whose paths are joined with
this route's |
resolve
¶
Resolve a pathname to the best matching route and params.
The router prefers the most specific segment match and honors a
base_path prefix when provided.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
routes
|
list[Any]
|
Flat or nested route specs (any object exposing
|
required |
pathname
|
str
|
The current URL pathname. |
required |
base_path
|
str
|
Optional base path stripped from |
''
|
Returns:
| Type | Description |
|---|---|
tuple[Any, dict[str, Any]] | None
|
A tuple |
tuple[Any, dict[str, Any]] | None
|
|
navigate
¶
Navigate within this origin, resolving relative URLs and preserving hashes.
External URLs use normal browser navigation. Back/forward restores recorded scroll positions; ordinary navigation scrolls to a hash target or the top once the destination's transition commits. A no-op during a server render.
use_params
¶
Accessor for the matched route's params ({} outside a router).
use_query
¶
use_query() -> Accessor[QueryParams]
Accessor for the parsed query string ({} outside a router).
Router
¶
Render the component of the route matching current_path.
Only a change in which route matches re-mounts the outlet; param and query changes flow into the mounted component as prop updates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
routes
|
Any
|
A list of |
required |
base_path
|
Any
|
Base path stripped before matching, and prepended
by |
''
|
not_found
|
Any
|
Optional component rendered when no route matches.
Falls back to a literal |
None
|
Returns:
| Type | Description |
|---|---|
VNode
|
A component |
use_hash
¶
Read the decoded current URL fragment, without its leading hash.
Link
¶
Link(*children: Any, href: Any = '/', replace: bool = False, active_class: str | None = 'active', end: bool = False, **rest: Any) -> VNode
An anchor that navigates with the History API and marks itself active.
Modifier-key clicks (Cmd/Ctrl/Shift) and middle-clicks are passed through to the browser so users can open links in a new tab.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*children
|
Any
|
Link content. |
()
|
href
|
Any
|
Target path (or an accessor). Joined with the surrounding
router's base path unless it starts with |
'/'
|
replace
|
bool
|
Replace the current history entry instead of pushing. |
False
|
active_class
|
str | None
|
Class added while the link matches the current
path. |
'active'
|
end
|
bool
|
When |
False
|
**rest
|
Any
|
Forwarded to the |
{}
|
What's in this module¶
A client-side router built on the History API, plus the pure path
matcher behind it. Import it from wybthon.router; these names aren't
re-exported from wybthon. Only a change in which route matches
remounts the outlet; param and query changes flow into the mounted
component as prop updates, so navigating from /users/1 to /users/2
keeps the same component instance.
| Name | Description |
|---|---|
Route |
Route(path, component, children=[], preload=None); patterns support :param and a * wildcard. preload(params) warms data before the route shows. |
RouteProps |
Props for a routed component: params: Prop[dict[str, str]] and query: Prop[QueryParams]. Subclass it to add fields. |
Router |
Router(routes, *, base_path="", not_found=None); renders the matched component with params and query props. |
Outlet |
Renders the next matched child route inside a parent route's layout. |
Link |
Link(*children, href="/", replace=False, active_class="active", end=False, **rest); an <a> that navigates and marks itself active. |
navigate |
navigate(path, *, replace=False, scroll=True); push or replace a history entry and update current_path. |
current_path |
Accessor for the pathname plus query string; updated by navigate and popstate. |
use_params |
Accessor for the matched route's params dict ({} outside a router). |
use_query |
Accessor for the parsed QueryParams (empty outside a router). |
use_hash |
Accessor for the decoded URL fragment, without its #. |
use_base_path |
The surrounding router's base path ("" outside one). |
preload |
preload(path): warm a route's lazy component and data through the surrounding router. |
QueryParams |
A dict of the last value per key, plus get_all(key) for repeated keys. |
resolve, RouteSpec |
The browser-independent matcher, usable in tests, tools, and on a server. |
from wybthon import component, div, h1, http_status, nav
from wybthon.router import Link, Outlet, Route, RouteProps, Router
@component
def Home():
return h1("Home")
@component
def Users():
return div(h1("Users"), Outlet()) # child routes render here
@component
def User(props: RouteProps):
return h1(
"User ",
lambda: props.params()["id"],
lambda: f" (tab={props.query().get('tab', 'info')})",
)
@component
def NotFound(props: RouteProps):
http_status(404) # sets the status during a server render
return h1("Not found")
@component
def App():
return div(
nav(Link("Home", href="/", end=True), Link("Ada", href="/users/1?tab=posts")),
Router(
[Route("/", Home), Route("/users", Users, children=[Route(":id", User)]), Route("/docs/*", Home)],
base_path="/app",
not_found=NotFound,
),
)
- A trailing
/*captures the rest intoparams["wildcard"]and also matches the parent path (/docs/*matches/docs). Nestedchildrenroutes join their paths with the parent's; the parent renders the child throughOutlet(). Linkjoinshrefwith the router'sbase_pathunless it starts withhttp://,https://, or#. Modifier-key and middle clicks fall through to the browser.end=Truemakes the active class exact-match only. ALink's ownon_clickreceives the event.- Outside a browser,
navigateonly updates thecurrent_pathsignal, and during a server render it's a no-op; callflushafterward in tests.
Path matching¶
resolve(routes, pathname, base_path="")
returns (route, {"params": {...}, "matches": [...]}) or None, where
matches lists the matched route and its ancestors. Any object with
path and children attributes works as a route, including
Route and the minimal
RouteSpec.
| Pattern | Matches | Params |
|---|---|---|
/users |
/users |
{} |
/users/:id |
/users/42 |
{"id": "42"} |
/docs/* |
/docs, /docs/intro, /docs/a/b |
{"wildcard": ""} or {"wildcard": "intro"} |
/files/*/raw |
/files/a/b/raw |
{"wildcard": "a/b"} |
Nested children join their paths with the parent's (a child path
starting with / is absolute). When several routes match, the most
specific pattern wins. base_path is stripped before matching; a
pathname outside the base returns None.
from wybthon.router import RouteSpec, resolve
routes = [
RouteSpec("/"),
RouteSpec("/users", children=[RouteSpec(":id")]),
RouteSpec("/docs/*"),
]
resolve(routes, "/users/42") # (RouteSpec(path=':id', ...), {"params": {"id": "42"}, "matches": [...]})
resolve(routes, "/docs/intro/setup") # (..., {"params": {"wildcard": "intro/setup"}, ...})
resolve(routes, "/app/users/7", base_path="/app")
resolve(routes, "/missing") # None
Param values are the raw matched segments; the browser router decodes query strings separately.
See also¶
- Lazy loading: code-split route components
- Request:
http_statusandhttp_headerfor not-found and redirect responses - Concepts: Router
- Examples: Router