Routing
Routing in PreffX is driven by a root-level url signal. Navigation updates it, and the rendered tree reacts automatically. The library is agnostic to the navigation source: it works with the browser Navigation API when available and falls back to detached, signal-managed navigation in SSR or headless environments.
Reading the URL
The url utility is a read-only URL signal:
import type { PC } from 'preffx';
export const Path: PC = (_props, { url }) => {
return <p>You are at: {url.value.pathname}</p>;
};Triggering navigation
Use navigate to move to another URL:
import type { PC } from 'preffx';
export const Go: PC = (_props, { navigate }) => {
return <button onClick={() => navigate('/about')}>About</button>;
};Declarative routes with routes
The routes utility takes a map of path pattern → component and returns a reactive signal holding the rendered content for the first matching route. Matching is first-match-wins: patterns are checked in object key order.
import type { PC } from 'preffx';
export const App: PC = (_props, { routes }) => {
const content = routes({
'/': () => <div>Home</div>,
'/:lang?/user/:id': (_, { routeParams }) => (
<div>User #{routeParams.id} ({routeParams.lang || 'en'})</div>
),
'*': () => <div>Not found</div>
});
return (
<div>
<a href="/">Home</a>
<a href="/user/42">User page</a>
<a href="/ru/user/42">User page (ru)</a>
<a href="/unknown">Unknown page</a>
{content}
</div>
);
};Route patterns
| Pattern | Description | Example match |
|---|---|---|
/users | Static segment | /users |
/user/:id | Required parameter (:id) | /user/42 → { id: '42' } |
/:lang?/home | Optional parameter (?); empty when absent | /home → { lang: '' } |
/files/* | Splat — captures the rest (from the trailing *) | /files/a/b → { '*': 'a/b' } |
* | Catch-all — matches any path | /anything |
Notes:
- Route parameters are URI-decoded automatically.
- A pattern matches a prefix of the pathname:
/usersalso matches/users/42. Use a catch-all or a more specific pattern when you need exact matching. *acts as a wildcard only as the last segment; elsewhere it is treated as a literal.- Matched route parameters are available through
routeParams. - In a nested layout, relative patterns are resolved against the parent route's matched path.
Nested routes
Call routes() in a parent component; the matched child component is rendered into the returned signal. Route params from the matched pattern are exposed via routeParams and are also available to descendants through context.
Utils summary
| Option | Type | Description |
|---|---|---|
defaultURL | string | URL | Initial URL (for SSR pass the request URL) |
url | ReadonlySignal<URL> | Current URL, reactive |
navigate | (url, options?) | Navigate to a URL |
routes | (paths) => signal | First-match route rendering |
routeParams | Record<string, string> | Parameters of the currently matched route |