@rangojs/router 0.0.0-experimental.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/CLAUDE.md +43 -0
  2. package/README.md +19 -0
  3. package/dist/bin/rango.js +227 -0
  4. package/dist/vite/index.js +3039 -0
  5. package/package.json +171 -0
  6. package/skills/caching/SKILL.md +191 -0
  7. package/skills/debug-manifest/SKILL.md +108 -0
  8. package/skills/document-cache/SKILL.md +180 -0
  9. package/skills/fonts/SKILL.md +165 -0
  10. package/skills/hooks/SKILL.md +442 -0
  11. package/skills/intercept/SKILL.md +190 -0
  12. package/skills/layout/SKILL.md +213 -0
  13. package/skills/links/SKILL.md +180 -0
  14. package/skills/loader/SKILL.md +246 -0
  15. package/skills/middleware/SKILL.md +202 -0
  16. package/skills/mime-routes/SKILL.md +124 -0
  17. package/skills/parallel/SKILL.md +228 -0
  18. package/skills/prerender/SKILL.md +283 -0
  19. package/skills/rango/SKILL.md +54 -0
  20. package/skills/response-routes/SKILL.md +358 -0
  21. package/skills/route/SKILL.md +173 -0
  22. package/skills/router-setup/SKILL.md +346 -0
  23. package/skills/tailwind/SKILL.md +129 -0
  24. package/skills/theme/SKILL.md +78 -0
  25. package/skills/typesafety/SKILL.md +394 -0
  26. package/src/__internal.ts +175 -0
  27. package/src/bin/rango.ts +24 -0
  28. package/src/browser/event-controller.ts +876 -0
  29. package/src/browser/index.ts +18 -0
  30. package/src/browser/link-interceptor.ts +121 -0
  31. package/src/browser/lru-cache.ts +69 -0
  32. package/src/browser/merge-segment-loaders.ts +126 -0
  33. package/src/browser/navigation-bridge.ts +913 -0
  34. package/src/browser/navigation-client.ts +165 -0
  35. package/src/browser/navigation-store.ts +823 -0
  36. package/src/browser/partial-update.ts +600 -0
  37. package/src/browser/react/Link.tsx +248 -0
  38. package/src/browser/react/NavigationProvider.tsx +346 -0
  39. package/src/browser/react/ScrollRestoration.tsx +94 -0
  40. package/src/browser/react/context.ts +53 -0
  41. package/src/browser/react/index.ts +52 -0
  42. package/src/browser/react/location-state-shared.ts +120 -0
  43. package/src/browser/react/location-state.ts +62 -0
  44. package/src/browser/react/mount-context.ts +32 -0
  45. package/src/browser/react/use-action.ts +240 -0
  46. package/src/browser/react/use-client-cache.ts +56 -0
  47. package/src/browser/react/use-handle.ts +203 -0
  48. package/src/browser/react/use-href.tsx +40 -0
  49. package/src/browser/react/use-link-status.ts +134 -0
  50. package/src/browser/react/use-mount.ts +31 -0
  51. package/src/browser/react/use-navigation.ts +140 -0
  52. package/src/browser/react/use-segments.ts +188 -0
  53. package/src/browser/request-controller.ts +164 -0
  54. package/src/browser/rsc-router.tsx +352 -0
  55. package/src/browser/scroll-restoration.ts +324 -0
  56. package/src/browser/segment-structure-assert.ts +67 -0
  57. package/src/browser/server-action-bridge.ts +762 -0
  58. package/src/browser/shallow.ts +35 -0
  59. package/src/browser/types.ts +478 -0
  60. package/src/build/generate-manifest.ts +377 -0
  61. package/src/build/generate-route-types.ts +828 -0
  62. package/src/build/index.ts +36 -0
  63. package/src/build/route-trie.ts +239 -0
  64. package/src/cache/cache-scope.ts +563 -0
  65. package/src/cache/cf/cf-cache-store.ts +428 -0
  66. package/src/cache/cf/index.ts +19 -0
  67. package/src/cache/document-cache.ts +340 -0
  68. package/src/cache/index.ts +58 -0
  69. package/src/cache/memory-segment-store.ts +150 -0
  70. package/src/cache/memory-store.ts +253 -0
  71. package/src/cache/types.ts +392 -0
  72. package/src/client.rsc.tsx +83 -0
  73. package/src/client.tsx +643 -0
  74. package/src/component-utils.ts +76 -0
  75. package/src/components/DefaultDocument.tsx +23 -0
  76. package/src/debug.ts +233 -0
  77. package/src/default-error-boundary.tsx +88 -0
  78. package/src/deps/browser.ts +8 -0
  79. package/src/deps/html-stream-client.ts +2 -0
  80. package/src/deps/html-stream-server.ts +2 -0
  81. package/src/deps/rsc.ts +10 -0
  82. package/src/deps/ssr.ts +2 -0
  83. package/src/errors.ts +295 -0
  84. package/src/handle.ts +130 -0
  85. package/src/handles/MetaTags.tsx +193 -0
  86. package/src/handles/index.ts +6 -0
  87. package/src/handles/meta.ts +247 -0
  88. package/src/host/cookie-handler.ts +159 -0
  89. package/src/host/errors.ts +97 -0
  90. package/src/host/index.ts +56 -0
  91. package/src/host/pattern-matcher.ts +214 -0
  92. package/src/host/router.ts +330 -0
  93. package/src/host/testing.ts +79 -0
  94. package/src/host/types.ts +138 -0
  95. package/src/host/utils.ts +25 -0
  96. package/src/href-client.ts +202 -0
  97. package/src/href-context.ts +33 -0
  98. package/src/index.rsc.ts +121 -0
  99. package/src/index.ts +165 -0
  100. package/src/loader.rsc.ts +207 -0
  101. package/src/loader.ts +47 -0
  102. package/src/network-error-thrower.tsx +21 -0
  103. package/src/outlet-context.ts +15 -0
  104. package/src/prerender/param-hash.ts +35 -0
  105. package/src/prerender/store.ts +40 -0
  106. package/src/prerender.ts +156 -0
  107. package/src/reverse.ts +267 -0
  108. package/src/root-error-boundary.tsx +277 -0
  109. package/src/route-content-wrapper.tsx +193 -0
  110. package/src/route-definition.ts +1431 -0
  111. package/src/route-map-builder.ts +242 -0
  112. package/src/route-types.ts +220 -0
  113. package/src/router/error-handling.ts +287 -0
  114. package/src/router/handler-context.ts +158 -0
  115. package/src/router/intercept-resolution.ts +387 -0
  116. package/src/router/loader-resolution.ts +327 -0
  117. package/src/router/manifest.ts +216 -0
  118. package/src/router/match-api.ts +621 -0
  119. package/src/router/match-context.ts +264 -0
  120. package/src/router/match-middleware/background-revalidation.ts +236 -0
  121. package/src/router/match-middleware/cache-lookup.ts +382 -0
  122. package/src/router/match-middleware/cache-store.ts +276 -0
  123. package/src/router/match-middleware/index.ts +81 -0
  124. package/src/router/match-middleware/intercept-resolution.ts +281 -0
  125. package/src/router/match-middleware/segment-resolution.ts +184 -0
  126. package/src/router/match-pipelines.ts +214 -0
  127. package/src/router/match-result.ts +213 -0
  128. package/src/router/metrics.ts +62 -0
  129. package/src/router/middleware.ts +791 -0
  130. package/src/router/pattern-matching.ts +407 -0
  131. package/src/router/revalidation.ts +190 -0
  132. package/src/router/router-context.ts +301 -0
  133. package/src/router/segment-resolution.ts +1315 -0
  134. package/src/router/trie-matching.ts +172 -0
  135. package/src/router/types.ts +163 -0
  136. package/src/router.gen.ts +6 -0
  137. package/src/router.ts +2423 -0
  138. package/src/rsc/handler.ts +1443 -0
  139. package/src/rsc/helpers.ts +64 -0
  140. package/src/rsc/index.ts +56 -0
  141. package/src/rsc/nonce.ts +18 -0
  142. package/src/rsc/types.ts +236 -0
  143. package/src/segment-system.tsx +442 -0
  144. package/src/server/context.ts +466 -0
  145. package/src/server/handle-store.ts +229 -0
  146. package/src/server/loader-registry.ts +174 -0
  147. package/src/server/request-context.ts +554 -0
  148. package/src/server/root-layout.tsx +10 -0
  149. package/src/server/tsconfig.json +14 -0
  150. package/src/server.ts +171 -0
  151. package/src/ssr/index.tsx +296 -0
  152. package/src/theme/ThemeProvider.tsx +291 -0
  153. package/src/theme/ThemeScript.tsx +61 -0
  154. package/src/theme/constants.ts +59 -0
  155. package/src/theme/index.ts +58 -0
  156. package/src/theme/theme-context.ts +70 -0
  157. package/src/theme/theme-script.ts +152 -0
  158. package/src/theme/types.ts +182 -0
  159. package/src/theme/use-theme.ts +44 -0
  160. package/src/types.ts +1757 -0
  161. package/src/urls.gen.ts +8 -0
  162. package/src/urls.ts +1282 -0
  163. package/src/use-loader.tsx +346 -0
  164. package/src/vite/expose-action-id.ts +344 -0
  165. package/src/vite/expose-handle-id.ts +209 -0
  166. package/src/vite/expose-loader-id.ts +426 -0
  167. package/src/vite/expose-location-state-id.ts +177 -0
  168. package/src/vite/expose-prerender-handler-id.ts +429 -0
  169. package/src/vite/index.ts +2068 -0
  170. package/src/vite/package-resolution.ts +125 -0
  171. package/src/vite/version.d.ts +12 -0
  172. package/src/vite/virtual-entries.ts +114 -0
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: intercept
3
+ description: Define intercept routes for modals, slide-overs, and soft navigation patterns in @rangojs/router
4
+ argument-hint: [@slot-name] [route-to-intercept]
5
+ ---
6
+
7
+ # Intercept Routes
8
+
9
+ Intercept routes render a different component during soft navigation (client-side) while preserving the background route. Hard navigation (direct URL) shows the full page.
10
+
11
+ ## Basic Intercept
12
+
13
+ ```typescript
14
+ import { urls } from "@rangojs/router";
15
+ import { Outlet, ParallelOutlet } from "@rangojs/router/client";
16
+
17
+ function ShopLayout() {
18
+ return (
19
+ <div className="shop">
20
+ <Outlet />
21
+ <ParallelOutlet name="@modal" />
22
+ </div>
23
+ );
24
+ }
25
+
26
+ export const urlpatterns = urls(({ path, layout, intercept, loader }) => [
27
+ layout(<ShopLayout />, () => [
28
+ // Intercept product detail - shows modal during soft navigation
29
+ intercept(
30
+ "@modal", // Slot name
31
+ "product", // Route name to intercept
32
+ <ProductModal />, // Modal component
33
+ () => [
34
+ loader(ProductLoader),
35
+ loading(<ProductModalSkeleton />),
36
+ ]
37
+ ),
38
+
39
+ // Normal routes
40
+ path("/shop", ShopIndex, { name: "index" }),
41
+ path("/shop/product/:slug", ProductPage, { name: "product" }),
42
+ ]),
43
+ ]);
44
+ ```
45
+
46
+ ## Navigation Behavior
47
+
48
+ | Navigation Type | What Renders |
49
+ |-----------------|--------------|
50
+ | Click link `/shop/product/abc` | `<ProductModal />` in `@modal`, background preserved |
51
+ | Direct URL `/shop/product/abc` | Full `<ProductPage />` page |
52
+ | Browser back | Close modal, restore previous state |
53
+
54
+ ## Intercept with Layout
55
+
56
+ Wrap intercept content in a modal layout:
57
+
58
+ ```typescript
59
+ intercept(
60
+ "@modal",
61
+ "product",
62
+ <ProductModalContent />,
63
+ () => [
64
+ layout(<ModalWrapper />), // Wraps the modal content
65
+ loader(ProductLoader),
66
+ loading(<ProductModalSkeleton />),
67
+ ]
68
+ )
69
+ ```
70
+
71
+ ## Conditional Intercept with when()
72
+
73
+ Only intercept based on navigation context:
74
+
75
+ ```typescript
76
+ intercept(
77
+ "@modal",
78
+ "product",
79
+ <ProductModal />,
80
+ () => [
81
+ // Only intercept when coming from a different section
82
+ when(({ from }) => !from.pathname.startsWith("/shop/product/")),
83
+ loader(ProductLoader),
84
+ ]
85
+ )
86
+ ```
87
+
88
+ ## Multiple Loaders in Intercept
89
+
90
+ ```typescript
91
+ intercept(
92
+ "@modal",
93
+ "product",
94
+ <ProductModal />,
95
+ () => [
96
+ loader(ProductLoader, () => [cache()]),
97
+ loader(ProductCartLoader, () => [revalidate(() => true)]),
98
+ loader(RecommendationsLoader),
99
+ ]
100
+ )
101
+ ```
102
+
103
+ ## Closing the Modal
104
+
105
+ Use navigation to close:
106
+
107
+ ```typescript
108
+ "use client";
109
+ import { useNavigation } from "@rangojs/router/client";
110
+
111
+ function ModalWrapper({ children }) {
112
+ const { goBack } = useNavigation();
113
+
114
+ return (
115
+ <div className="modal-overlay" onClick={goBack}>
116
+ <div className="modal" onClick={(e) => e.stopPropagation()}>
117
+ <button onClick={goBack}>Close</button>
118
+ {children}
119
+ </div>
120
+ </div>
121
+ );
122
+ }
123
+ ```
124
+
125
+ ## Complete Example
126
+
127
+ ```typescript
128
+ // components/ProductModal.tsx
129
+ import { Outlet, ParallelOutlet } from "@rangojs/router/client";
130
+
131
+ function ShopLayout() {
132
+ return (
133
+ <div className="shop">
134
+ <ParallelOutlet name="@promoBanner" />
135
+ <main>
136
+ <Outlet />
137
+ </main>
138
+ <ParallelOutlet name="@modal" />
139
+ </div>
140
+ );
141
+ }
142
+
143
+ function ModalWrapper({ children }) {
144
+ return (
145
+ <div className="modal-overlay">
146
+ <div className="modal">{children}</div>
147
+ </div>
148
+ );
149
+ }
150
+
151
+ // urls/shop.tsx
152
+ import { urls } from "@rangojs/router";
153
+
154
+ export const shopPatterns = urls(({
155
+ path,
156
+ layout,
157
+ parallel,
158
+ intercept,
159
+ loader,
160
+ loading,
161
+ when,
162
+ }) => [
163
+ layout(<ShopLayout />, () => [
164
+ parallel({
165
+ "@promoBanner": () => <PromoBanner />,
166
+ }),
167
+
168
+ // Intercept product detail into modal
169
+ intercept(
170
+ "@modal",
171
+ "product", // Route name (without prefix)
172
+ <ProductModalContent />,
173
+ () => [
174
+ when(({ from }) => !from.pathname.startsWith("/shop/product/")),
175
+ layout(<ModalWrapper />),
176
+ loading(<ProductModalSkeleton />),
177
+ loader(ProductLoader, () => [cache()]),
178
+ loader(RecommendationsLoader),
179
+ ]
180
+ ),
181
+
182
+ // Normal routes
183
+ path("/", ShopIndex, { name: "index" }),
184
+ path("/product/:slug", ProductPage, { name: "product" }, () => [
185
+ loader(ProductLoader),
186
+ loading(<ProductPageSkeleton />),
187
+ ]),
188
+ ]),
189
+ ]);
190
+ ```
@@ -0,0 +1,213 @@
1
+ ---
2
+ name: layout
3
+ description: Define layout routes that wrap child routes in @rangojs/router
4
+ argument-hint: [component]
5
+ ---
6
+
7
+ # Layouts with layout()
8
+
9
+ Layouts wrap child routes and persist during navigation within their scope.
10
+
11
+ ## Basic Layout
12
+
13
+ ```typescript
14
+ import { urls } from "@rangojs/router";
15
+ import { Outlet } from "@rangojs/router/client";
16
+
17
+ function ShopLayout() {
18
+ return (
19
+ <div className="shop">
20
+ <nav>Shop Navigation</nav>
21
+ <Outlet /> {/* Child routes render here */}
22
+ </div>
23
+ );
24
+ }
25
+
26
+ export const urlpatterns = urls(({ path, layout }) => [
27
+ layout(<ShopLayout />, () => [
28
+ path("/shop", ShopIndex, { name: "shop.index" }),
29
+ path("/shop/cart", CartPage, { name: "shop.cart" }),
30
+ path("/shop/product/:slug", ProductPage, { name: "shop.product" }),
31
+ ]),
32
+ ]);
33
+ ```
34
+
35
+ ## Layout Patterns
36
+
37
+ ### JSX Element
38
+
39
+ ```typescript
40
+ layout(<ShopLayout />, () => [
41
+ path("/shop", ShopIndex, { name: "shop" }),
42
+ ])
43
+ ```
44
+
45
+ ### Component Function
46
+
47
+ ```typescript
48
+ layout(ShopLayout, () => [
49
+ path("/shop", ShopIndex, { name: "shop" }),
50
+ ])
51
+ ```
52
+
53
+ ### Handler with Context
54
+
55
+ ```typescript
56
+ layout((ctx) => {
57
+ const push = ctx.use(Breadcrumbs);
58
+ push({ label: "Shop", href: "/shop" });
59
+ return <ShopLayout />;
60
+ }, () => [
61
+ path("/shop", ShopIndex, { name: "shop" }),
62
+ ])
63
+ ```
64
+
65
+ ## Nested Layouts
66
+
67
+ Layouts compose by wrapping order (first layout wraps outer):
68
+
69
+ ```typescript
70
+ urls(({ path, layout }) => [
71
+ layout(<RootLayout />, () => [ // Outer
72
+ layout(<ShopLayout />, () => [ // Inner
73
+ path("/shop", ShopIndex, { name: "shop" }),
74
+ ]),
75
+ ]),
76
+ ])
77
+
78
+ // Result: RootLayout > ShopLayout > ShopIndex
79
+ ```
80
+
81
+ ## Layout with Children DSL
82
+
83
+ Add loaders, parallel routes, or revalidation to layouts:
84
+
85
+ ```typescript
86
+ layout(<ShopLayout />, () => [
87
+ // Loaders for layout
88
+ loader(CartLoader),
89
+ loader(UserLoader),
90
+
91
+ // Revalidation rules for layout
92
+ revalidate(shopRevalidation),
93
+
94
+ // Child routes
95
+ path("/shop", ShopIndex, { name: "shop" }),
96
+ path("/shop/cart", CartPage, { name: "cart" }),
97
+ ])
98
+ ```
99
+
100
+ ## The Outlet Component
101
+
102
+ `<Outlet />` renders child content. Import from `@rangojs/router/client`:
103
+
104
+ ```typescript
105
+ import { Outlet } from "@rangojs/router/client";
106
+
107
+ function ShopLayout() {
108
+ return (
109
+ <div className="shop-layout">
110
+ <header>Shop Header</header>
111
+ <main>
112
+ <Outlet /> {/* Child routes render here */}
113
+ </main>
114
+ <footer>Shop Footer</footer>
115
+ </div>
116
+ );
117
+ }
118
+ ```
119
+
120
+ ## Named Outlets
121
+
122
+ For parallel routes, use named outlets:
123
+
124
+ ```typescript
125
+ import { Outlet, ParallelOutlet } from "@rangojs/router/client";
126
+
127
+ function DashboardLayout() {
128
+ return (
129
+ <div className="dashboard">
130
+ <aside>
131
+ <ParallelOutlet name="@sidebar" />
132
+ </aside>
133
+ <main>
134
+ <Outlet /> {/* Main content */}
135
+ </main>
136
+ <aside>
137
+ <ParallelOutlet name="@notifications" />
138
+ </aside>
139
+ </div>
140
+ );
141
+ }
142
+ ```
143
+
144
+ ## Layout Revalidation
145
+
146
+ Layouts don't revalidate by default. Control with `revalidate()`:
147
+
148
+ ```typescript
149
+ layout(<ShopLayout />, () => [
150
+ // Never revalidate (default behavior)
151
+ revalidate(() => false),
152
+
153
+ path("/shop", ShopIndex, { name: "shop" }),
154
+ ])
155
+
156
+ // Or revalidate based on conditions
157
+ layout(<CartLayout />, () => [
158
+ revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
159
+
160
+ path("/cart", CartPage, { name: "cart" }),
161
+ ])
162
+ ```
163
+
164
+ ## Complete Example
165
+
166
+ ```typescript
167
+ import { urls } from "@rangojs/router";
168
+ import { Outlet, ParallelOutlet } from "@rangojs/router/client";
169
+
170
+ function ShopLayout() {
171
+ return (
172
+ <div className="shop">
173
+ <ParallelOutlet name="@promoBanner" />
174
+ <nav>
175
+ <a href="/shop">Home</a>
176
+ <a href="/shop/cart">Cart</a>
177
+ </nav>
178
+ <div className="content">
179
+ <aside>
180
+ <ParallelOutlet name="@sidebar" />
181
+ </aside>
182
+ <main>
183
+ <Outlet />
184
+ </main>
185
+ </div>
186
+ </div>
187
+ );
188
+ }
189
+
190
+ export const shopPatterns = urls(({ path, layout, parallel, loader, revalidate }) => [
191
+ layout((ctx) => {
192
+ const push = ctx.use(Breadcrumbs);
193
+ push({ label: "Shop", href: "/shop" });
194
+ return <ShopLayout />;
195
+ }, () => [
196
+ // Layout loaders
197
+ loader(CartLoader, () => [
198
+ revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
199
+ ]),
200
+
201
+ // Parallel routes
202
+ parallel({
203
+ "@promoBanner": () => <PromoBanner />,
204
+ "@sidebar": () => <CategorySidebar />,
205
+ }),
206
+
207
+ // Child routes
208
+ path("/shop", ShopIndex, { name: "index" }),
209
+ path("/shop/cart", CartPage, { name: "cart" }),
210
+ path("/shop/product/:slug", ProductPage, { name: "product" }),
211
+ ]),
212
+ ]);
213
+ ```
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: links
3
+ description: URL generation with ctx.reverse (server), href (client), useHref (mounted), useMount, and scopedReverse
4
+ argument-hint: [href|useHref|useMount|scopedReverse]
5
+ ---
6
+
7
+ # Links & URL Generation
8
+
9
+ @rangojs/router provides different href APIs for server and client contexts.
10
+
11
+ ## Server: ctx.reverse()
12
+
13
+ Available in route handlers via HandlerContext. Resolves named routes using the full route map.
14
+
15
+ ```typescript
16
+ import { urls, scopedReverse } from "@rangojs/router";
17
+
18
+ export const shopPatterns = urls(({ path, layout }) => [
19
+ layout(<ShopLayout />, () => [
20
+ path("/", ShopIndex, { name: "index" }),
21
+ path("/cart", CartPage, { name: "cart" }),
22
+ path("/product/:slug", ProductPage, { name: "product" }),
23
+ ]),
24
+ ]);
25
+ ```
26
+
27
+ ### Resolution priority
28
+
29
+ 1. **Path-based** (`/...`) - returned as-is
30
+ 2. **Absolute name** (contains dot: `blog.post`) - global lookup
31
+ 3. **Local name** (`cart`) - resolved relative to current route's namespace
32
+
33
+ ```typescript
34
+ // Inside a handler within shopPatterns (mounted at /shop)
35
+ path("/product/:slug", (ctx) => {
36
+ ctx.reverse("cart"); // "/shop/cart" (local)
37
+ ctx.reverse("product", { slug: "widget" }); // "/shop/product/widget" (local + params)
38
+ ctx.reverse("blog.post", { slug: "hi" }); // "/blog/hi" (absolute)
39
+ ctx.reverse("/about"); // "/about" (path-based)
40
+
41
+ return <ProductPage slug={ctx.params.slug} />;
42
+ }, { name: "product" })
43
+ ```
44
+
45
+ ### scopedReverse() - type-safe ctx.reverse
46
+
47
+ Wraps `ctx.reverse` with local route type information for autocomplete and validation:
48
+
49
+ ```typescript
50
+ import { scopedReverse } from "@rangojs/router";
51
+
52
+ path("/product/:slug", (ctx) => {
53
+ const reverse = scopedReverse<typeof shopPatterns>(ctx.reverse);
54
+
55
+ reverse("cart"); // Type-safe local name
56
+ reverse("product", { slug: "widget" }); // Type-safe with params
57
+ reverse("blog.post"); // Absolute names (dot notation) always allowed
58
+ reverse("/about"); // Path-based always allowed
59
+
60
+ return <ProductPage slug={ctx.params.slug} />;
61
+ }, { name: "product" })
62
+ ```
63
+
64
+ ## Client: href()
65
+
66
+ Plain function for absolute path-based URLs. No hook needed - works anywhere.
67
+
68
+ ```typescript
69
+ "use client";
70
+ import { href, Link } from "@rangojs/router/client";
71
+
72
+ function GlobalNav() {
73
+ return (
74
+ <nav>
75
+ <Link to={href("/")}>Home</Link>
76
+ <Link to={href("/about")}>About</Link>
77
+ <Link to={href("/blog/hello")}>Post</Link>
78
+ </nav>
79
+ );
80
+ }
81
+ ```
82
+
83
+ `href()` is an identity function at runtime but provides compile-time validation via `ValidPaths` type. Paths are validated against registered route patterns using `PatternToPath`.
84
+
85
+ ## Client: useHref()
86
+
87
+ Hook that returns a mount-aware href function. Automatically prepends the `include()` mount prefix.
88
+
89
+ ```typescript
90
+ "use client";
91
+ import { useHref, href, Link } from "@rangojs/router/client";
92
+
93
+ // Inside include("/shop", shopPatterns)
94
+ function ShopNav() {
95
+ const href = useHref();
96
+
97
+ return (
98
+ <nav>
99
+ <Link to={href("/")}>Shop Home</Link> {/* "/shop/" */}
100
+ <Link to={href("/cart")}>Cart</Link> {/* "/shop/cart" */}
101
+ <Link to={href("/product/widget")}>W</Link> {/* "/shop/product/widget" */}
102
+ </nav>
103
+ );
104
+ }
105
+ ```
106
+
107
+ Use `useHref()` for local navigation within a mounted module. Use the bare `href()` function for absolute paths outside the current mount.
108
+
109
+ ## Client: useMount()
110
+
111
+ Returns the current `include()` mount path. Useful for building custom logic based on mount location.
112
+
113
+ ```typescript
114
+ "use client";
115
+ import { useMount } from "@rangojs/router/client";
116
+
117
+ function MountInfo() {
118
+ const mount = useMount(); // "/shop" inside include("/shop", ...)
119
+ // "/" at root level
120
+
121
+ return <span>Mounted at: {mount}</span>;
122
+ }
123
+ ```
124
+
125
+ `useMount()` reads from `MountContext`, which is automatically set by `include()` in the segment tree.
126
+
127
+ ## When to use what
128
+
129
+ | Context | API | Resolves | Use for |
130
+ |---------|-----|----------|---------|
131
+ | Server handler | `ctx.reverse("name")` | Named routes (local + absolute) | Server-side URL generation |
132
+ | Server handler | `scopedReverse<T>(ctx.reverse)` | Same, with type safety | Type-safe server URLs |
133
+ | Client component | `href("/path")` | Absolute paths | Global navigation |
134
+ | Client component | `useHref()` | Mount-prefixed paths | Local navigation inside `include()` |
135
+ | Client component | `useMount()` | Raw mount path | Custom mount-aware logic |
136
+
137
+ ## Complete example: mounted module
138
+
139
+ ```typescript
140
+ // urls/shop.tsx (server)
141
+ import { urls, scopedReverse } from "@rangojs/router";
142
+
143
+ export const shopPatterns = urls(({ path, layout }) => [
144
+ layout((ctx) => {
145
+ const reverse = scopedReverse<typeof shopPatterns>(ctx.reverse);
146
+ return <ShopLayout cartUrl={reverse("cart")} />;
147
+ }, () => [
148
+ path("/", ShopIndex, { name: "index" }),
149
+ path("/cart", CartPage, { name: "cart" }),
150
+ path("/product/:slug", ProductPage, { name: "product" }),
151
+ ]),
152
+ ]);
153
+
154
+ // urls.tsx (server)
155
+ export const urlpatterns = urls(({ path, include }) => [
156
+ path("/", HomePage, { name: "home" }),
157
+ include("/shop", shopPatterns),
158
+ ]);
159
+ ```
160
+
161
+ ```tsx
162
+ // components/ShopNav.tsx (client)
163
+ "use client";
164
+ import { useHref, href, Link } from "@rangojs/router/client";
165
+
166
+ export function ShopNav() {
167
+ const localHref = useHref();
168
+
169
+ return (
170
+ <nav>
171
+ {/* Local paths - auto-prefixed with /shop */}
172
+ <Link to={localHref("/cart")}>Cart</Link>
173
+ <Link to={localHref("/product/widget")}>Widget</Link>
174
+
175
+ {/* Absolute path - no prefix */}
176
+ <Link to={href("/")}>Home</Link>
177
+ </nav>
178
+ );
179
+ }
180
+ ```