{"protocol":"clank-doc/1","frameworkVersion":"0.8.0","slug":"routing","title":"Routing","description":"tsx const router = createRouter({ routes: [ { path: \"/\", component: Home, title: \"Home\" }, { path: \"/users/:id\", component: User, title: match = User ${match.params.id} , load: async ({ params, signal }) = { const response = await fetch( /a","group":{"id":"framework","title":"Framework"},"url":"https://docs.clank.run/docs/routing","source":"docs/routing.md","headings":["Define and start a router","Patterns","Component props","Links and navigation","Loaders and cancellation","Guards","Base paths and non browser resolution"],"tableOfContents":[{"id":"define-and-start-a-router","title":"Define and start a router","level":2},{"id":"patterns","title":"Patterns","level":2},{"id":"component-props","title":"Component props","level":2},{"id":"links-and-navigation","title":"Links and navigation","level":2},{"id":"loaders-and-cancellation","title":"Loaders and cancellation","level":2},{"id":"guards","title":"Guards","level":2},{"id":"base-paths-and-non-browser-resolution","title":"Base paths and non-browser resolution","level":2}],"markdown":"# Routing\n\n## Define and start a router\n\n```tsx\nconst router = createRouter({\n  routes: [\n    { path: \"/\", component: Home, title: \"Home\" },\n    {\n      path: \"/users/:id\",\n      component: User,\n      title: match => `User ${match.params.id}`,\n      load: async ({ params, signal }) => {\n        const response = await fetch(`/api/users/${params.id}`, { signal });\n        return response.json();\n      },\n    },\n    { path: \"/docs/:page?\", component: Docs },\n    { path: \"*\", component: NotFound },\n  ],\n  loading: Loading,\n  error: LoadError,\n});\n\nconst stop = router.start();\nrender(root, <router.View />);\n```\n\nRoutes are checked in declaration order. Put a wildcard last.\n\n## Patterns\n\n- `/users/:id` captures one required segment.\n- `/docs/:page?` captures an optional segment.\n- `/files/*` captures the remainder as `wildcard`.\n- `*` matches every path.\n\nCaptured values are URI-decoded. `matchPath()` and `matchRoutes()` are exported for server and test use.\n\n## Component props\n\nThe matched component receives `{ route, params, query, data }`. Repeated query keys become arrays; a single value remains a string.\n\n## Links and navigation\n\n```tsx\n<router.Link to=\"/users/42\" class=\"underline\">Open user</router.Link>\n\nawait router.navigate(\"/users/42\");\nawait router.navigate(\"/login\", { replace: true, state: { from: \"/private\" } });\n```\n\nRouter links render ordinary anchors with `data-clank-link`. Modified clicks, downloads, explicit targets, external origins, and already-prevented events retain native browser behavior.\n\n## Loaders and cancellation\n\nNavigation sets the route to `loading`, passes an `AbortSignal` to its loader, then commits `ready` data. The previous loader is aborted on a newer navigation, and revision checks reject stale results even if the loader ignores abort.\n\n## Guards\n\n```ts\nguard: ({ from, params }) => {\n  if (!session.value) return `/login?next=/projects/${params.id}`;\n  return true;\n}\n```\n\nA guard may return `true`, `false`, or a redirect URL, synchronously or asynchronously. `false` cancels navigation. A string recursively navigates with replacement.\n\n## Base paths and non-browser resolution\n\nSet `base: \"/app\"` when hosted below an origin root. `matchRoutes()` and `resolve(url)` also work without a browser; relative URLs use a safe internal origin.\n"}