@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.70

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 (307) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +942 -4
  3. package/dist/bin/rango.js +1689 -0
  4. package/dist/vite/index.js +4951 -930
  5. package/package.json +70 -60
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +294 -0
  8. package/skills/caching/SKILL.md +93 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +92 -31
  18. package/skills/loader/SKILL.md +404 -44
  19. package/skills/middleware/SKILL.md +173 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +685 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +257 -14
  26. package/skills/router-setup/SKILL.md +210 -32
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +328 -89
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +102 -4
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/app-version.ts +14 -0
  36. package/src/browser/event-controller.ts +92 -64
  37. package/src/browser/history-state.ts +80 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +24 -4
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +20 -12
  42. package/src/browser/navigation-bridge.ts +296 -558
  43. package/src/browser/navigation-client.ts +179 -69
  44. package/src/browser/navigation-store.ts +73 -55
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +328 -313
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +150 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +160 -0
  53. package/src/browser/prefetch/resource-ready.ts +77 -0
  54. package/src/browser/rango-state.ts +112 -0
  55. package/src/browser/react/Link.tsx +230 -74
  56. package/src/browser/react/NavigationProvider.tsx +87 -11
  57. package/src/browser/react/context.ts +11 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +12 -12
  60. package/src/browser/react/location-state-shared.ts +95 -53
  61. package/src/browser/react/location-state.ts +60 -15
  62. package/src/browser/react/mount-context.ts +6 -1
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +29 -51
  66. package/src/browser/react/use-client-cache.ts +5 -3
  67. package/src/browser/react/use-handle.ts +30 -126
  68. package/src/browser/react/use-href.tsx +2 -2
  69. package/src/browser/react/use-link-status.ts +6 -5
  70. package/src/browser/react/use-navigation.ts +22 -63
  71. package/src/browser/react/use-params.ts +65 -0
  72. package/src/browser/react/use-pathname.ts +47 -0
  73. package/src/browser/react/use-router.ts +76 -0
  74. package/src/browser/react/use-search-params.ts +56 -0
  75. package/src/browser/react/use-segments.ts +80 -97
  76. package/src/browser/response-adapter.ts +73 -0
  77. package/src/browser/rsc-router.tsx +214 -58
  78. package/src/browser/scroll-restoration.ts +127 -52
  79. package/src/browser/segment-reconciler.ts +221 -0
  80. package/src/browser/segment-structure-assert.ts +16 -0
  81. package/src/browser/server-action-bridge.ts +510 -603
  82. package/src/browser/shallow.ts +6 -1
  83. package/src/browser/types.ts +141 -48
  84. package/src/browser/validate-redirect-origin.ts +29 -0
  85. package/src/build/generate-manifest.ts +235 -24
  86. package/src/build/generate-route-types.ts +39 -0
  87. package/src/build/index.ts +13 -0
  88. package/src/build/route-trie.ts +265 -0
  89. package/src/build/route-types/ast-helpers.ts +25 -0
  90. package/src/build/route-types/ast-route-extraction.ts +98 -0
  91. package/src/build/route-types/codegen.ts +102 -0
  92. package/src/build/route-types/include-resolution.ts +418 -0
  93. package/src/build/route-types/param-extraction.ts +48 -0
  94. package/src/build/route-types/per-module-writer.ts +128 -0
  95. package/src/build/route-types/router-processing.ts +618 -0
  96. package/src/build/route-types/scan-filter.ts +85 -0
  97. package/src/build/runtime-discovery.ts +231 -0
  98. package/src/cache/background-task.ts +34 -0
  99. package/src/cache/cache-key-utils.ts +44 -0
  100. package/src/cache/cache-policy.ts +125 -0
  101. package/src/cache/cache-runtime.ts +342 -0
  102. package/src/cache/cache-scope.ts +167 -309
  103. package/src/cache/cf/cf-cache-store.ts +571 -17
  104. package/src/cache/cf/index.ts +13 -3
  105. package/src/cache/document-cache.ts +116 -77
  106. package/src/cache/handle-capture.ts +81 -0
  107. package/src/cache/handle-snapshot.ts +41 -0
  108. package/src/cache/index.ts +1 -15
  109. package/src/cache/memory-segment-store.ts +191 -13
  110. package/src/cache/profile-registry.ts +73 -0
  111. package/src/cache/read-through-swr.ts +134 -0
  112. package/src/cache/segment-codec.ts +256 -0
  113. package/src/cache/taint.ts +153 -0
  114. package/src/cache/types.ts +72 -122
  115. package/src/client.rsc.tsx +3 -1
  116. package/src/client.tsx +105 -179
  117. package/src/component-utils.ts +4 -4
  118. package/src/components/DefaultDocument.tsx +5 -1
  119. package/src/context-var.ts +156 -0
  120. package/src/debug.ts +19 -9
  121. package/src/errors.ts +108 -2
  122. package/src/handle.ts +55 -29
  123. package/src/handles/MetaTags.tsx +73 -20
  124. package/src/handles/breadcrumbs.ts +66 -0
  125. package/src/handles/index.ts +1 -0
  126. package/src/handles/meta.ts +30 -13
  127. package/src/host/cookie-handler.ts +21 -15
  128. package/src/host/errors.ts +8 -8
  129. package/src/host/index.ts +4 -7
  130. package/src/host/pattern-matcher.ts +27 -27
  131. package/src/host/router.ts +61 -39
  132. package/src/host/testing.ts +8 -8
  133. package/src/host/types.ts +15 -7
  134. package/src/host/utils.ts +1 -1
  135. package/src/href-client.ts +119 -29
  136. package/src/index.rsc.ts +155 -19
  137. package/src/index.ts +223 -30
  138. package/src/internal-debug.ts +11 -0
  139. package/src/loader.rsc.ts +26 -157
  140. package/src/loader.ts +27 -10
  141. package/src/network-error-thrower.tsx +3 -1
  142. package/src/outlet-provider.tsx +45 -0
  143. package/src/prerender/param-hash.ts +37 -0
  144. package/src/prerender/store.ts +186 -0
  145. package/src/prerender.ts +524 -0
  146. package/src/reverse.ts +351 -0
  147. package/src/root-error-boundary.tsx +41 -29
  148. package/src/route-content-wrapper.tsx +7 -4
  149. package/src/route-definition/dsl-helpers.ts +982 -0
  150. package/src/route-definition/helper-factories.ts +200 -0
  151. package/src/route-definition/helpers-types.ts +434 -0
  152. package/src/route-definition/index.ts +55 -0
  153. package/src/route-definition/redirect.ts +101 -0
  154. package/src/route-definition/resolve-handler-use.ts +149 -0
  155. package/src/route-definition.ts +1 -1428
  156. package/src/route-map-builder.ts +217 -123
  157. package/src/route-name.ts +53 -0
  158. package/src/route-types.ts +70 -8
  159. package/src/router/content-negotiation.ts +215 -0
  160. package/src/router/debug-manifest.ts +72 -0
  161. package/src/router/error-handling.ts +9 -9
  162. package/src/router/find-match.ts +160 -0
  163. package/src/router/handler-context.ts +435 -86
  164. package/src/router/intercept-resolution.ts +402 -0
  165. package/src/router/lazy-includes.ts +237 -0
  166. package/src/router/loader-resolution.ts +356 -128
  167. package/src/router/logging.ts +251 -0
  168. package/src/router/manifest.ts +154 -35
  169. package/src/router/match-api.ts +555 -0
  170. package/src/router/match-context.ts +5 -3
  171. package/src/router/match-handlers.ts +440 -0
  172. package/src/router/match-middleware/background-revalidation.ts +108 -93
  173. package/src/router/match-middleware/cache-lookup.ts +459 -10
  174. package/src/router/match-middleware/cache-store.ts +98 -26
  175. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  176. package/src/router/match-middleware/segment-resolution.ts +80 -6
  177. package/src/router/match-pipelines.ts +10 -45
  178. package/src/router/match-result.ts +55 -33
  179. package/src/router/metrics.ts +240 -15
  180. package/src/router/middleware-cookies.ts +55 -0
  181. package/src/router/middleware-types.ts +220 -0
  182. package/src/router/middleware.ts +324 -369
  183. package/src/router/navigation-snapshot.ts +182 -0
  184. package/src/router/pattern-matching.ts +211 -43
  185. package/src/router/prerender-match.ts +502 -0
  186. package/src/router/preview-match.ts +98 -0
  187. package/src/router/request-classification.ts +310 -0
  188. package/src/router/revalidation.ts +137 -38
  189. package/src/router/route-snapshot.ts +245 -0
  190. package/src/router/router-context.ts +41 -21
  191. package/src/router/router-interfaces.ts +484 -0
  192. package/src/router/router-options.ts +618 -0
  193. package/src/router/router-registry.ts +24 -0
  194. package/src/router/segment-resolution/fresh.ts +743 -0
  195. package/src/router/segment-resolution/helpers.ts +268 -0
  196. package/src/router/segment-resolution/loader-cache.ts +199 -0
  197. package/src/router/segment-resolution/revalidation.ts +1373 -0
  198. package/src/router/segment-resolution/static-store.ts +67 -0
  199. package/src/router/segment-resolution.ts +21 -0
  200. package/src/router/segment-wrappers.ts +291 -0
  201. package/src/router/telemetry-otel.ts +299 -0
  202. package/src/router/telemetry.ts +300 -0
  203. package/src/router/timeout.ts +148 -0
  204. package/src/router/trie-matching.ts +239 -0
  205. package/src/router/types.ts +78 -3
  206. package/src/router.ts +740 -4252
  207. package/src/rsc/handler-context.ts +45 -0
  208. package/src/rsc/handler.ts +907 -797
  209. package/src/rsc/helpers.ts +140 -6
  210. package/src/rsc/index.ts +0 -20
  211. package/src/rsc/loader-fetch.ts +229 -0
  212. package/src/rsc/manifest-init.ts +90 -0
  213. package/src/rsc/nonce.ts +14 -0
  214. package/src/rsc/origin-guard.ts +141 -0
  215. package/src/rsc/progressive-enhancement.ts +391 -0
  216. package/src/rsc/response-error.ts +37 -0
  217. package/src/rsc/response-route-handler.ts +347 -0
  218. package/src/rsc/rsc-rendering.ts +246 -0
  219. package/src/rsc/runtime-warnings.ts +42 -0
  220. package/src/rsc/server-action.ts +356 -0
  221. package/src/rsc/ssr-setup.ts +128 -0
  222. package/src/rsc/types.ts +46 -11
  223. package/src/search-params.ts +230 -0
  224. package/src/segment-system.tsx +165 -17
  225. package/src/server/context.ts +315 -58
  226. package/src/server/cookie-store.ts +190 -0
  227. package/src/server/fetchable-loader-store.ts +37 -0
  228. package/src/server/handle-store.ts +113 -15
  229. package/src/server/loader-registry.ts +24 -64
  230. package/src/server/request-context.ts +607 -81
  231. package/src/server.ts +35 -130
  232. package/src/ssr/index.tsx +103 -30
  233. package/src/static-handler.ts +126 -0
  234. package/src/theme/ThemeProvider.tsx +21 -15
  235. package/src/theme/ThemeScript.tsx +5 -5
  236. package/src/theme/constants.ts +5 -2
  237. package/src/theme/index.ts +4 -14
  238. package/src/theme/theme-context.ts +4 -30
  239. package/src/theme/theme-script.ts +21 -18
  240. package/src/types/boundaries.ts +158 -0
  241. package/src/types/cache-types.ts +198 -0
  242. package/src/types/error-types.ts +192 -0
  243. package/src/types/global-namespace.ts +100 -0
  244. package/src/types/handler-context.ts +791 -0
  245. package/src/types/index.ts +88 -0
  246. package/src/types/loader-types.ts +210 -0
  247. package/src/types/route-config.ts +170 -0
  248. package/src/types/route-entry.ts +109 -0
  249. package/src/types/segments.ts +150 -0
  250. package/src/types.ts +1 -1623
  251. package/src/urls/include-helper.ts +197 -0
  252. package/src/urls/index.ts +53 -0
  253. package/src/urls/path-helper-types.ts +346 -0
  254. package/src/urls/path-helper.ts +364 -0
  255. package/src/urls/pattern-types.ts +107 -0
  256. package/src/urls/response-types.ts +116 -0
  257. package/src/urls/type-extraction.ts +372 -0
  258. package/src/urls/urls-function.ts +98 -0
  259. package/src/urls.ts +1 -802
  260. package/src/use-loader.tsx +161 -81
  261. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  262. package/src/vite/discovery/discover-routers.ts +348 -0
  263. package/src/vite/discovery/prerender-collection.ts +439 -0
  264. package/src/vite/discovery/route-types-writer.ts +258 -0
  265. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  266. package/src/vite/discovery/state.ts +117 -0
  267. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  268. package/src/vite/index.ts +15 -1129
  269. package/src/vite/plugin-types.ts +103 -0
  270. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  271. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  272. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  273. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  274. package/src/vite/plugins/expose-id-utils.ts +299 -0
  275. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  276. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  277. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  278. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  279. package/src/vite/plugins/expose-ids/types.ts +45 -0
  280. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  281. package/src/vite/plugins/performance-tracks.ts +88 -0
  282. package/src/vite/plugins/refresh-cmd.ts +127 -0
  283. package/src/vite/plugins/use-cache-transform.ts +323 -0
  284. package/src/vite/plugins/version-injector.ts +83 -0
  285. package/src/vite/plugins/version-plugin.ts +266 -0
  286. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  287. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  288. package/src/vite/rango.ts +462 -0
  289. package/src/vite/router-discovery.ts +918 -0
  290. package/src/vite/utils/ast-handler-extract.ts +517 -0
  291. package/src/vite/utils/banner.ts +36 -0
  292. package/src/vite/utils/bundle-analysis.ts +137 -0
  293. package/src/vite/utils/manifest-utils.ts +70 -0
  294. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  295. package/src/vite/utils/prerender-utils.ts +207 -0
  296. package/src/vite/utils/shared-utils.ts +170 -0
  297. package/CLAUDE.md +0 -43
  298. package/src/browser/lru-cache.ts +0 -69
  299. package/src/browser/request-controller.ts +0 -164
  300. package/src/cache/memory-store.ts +0 -253
  301. package/src/href-context.ts +0 -33
  302. package/src/href.ts +0 -255
  303. package/src/server/route-manifest-cache.ts +0 -173
  304. package/src/vite/expose-handle-id.ts +0 -209
  305. package/src/vite/expose-loader-id.ts +0 -426
  306. package/src/vite/expose-location-state-id.ts +0 -177
  307. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -11,6 +11,7 @@ Inspect the route manifest to verify parent relationships, shortCodes, and route
11
11
  ## Quick Access
12
12
 
13
13
  In development, visit:
14
+
14
15
  ```
15
16
  http://localhost:PORT/__debug_manifest
16
17
  ```
@@ -62,13 +63,13 @@ if (process.env.NODE_ENV !== "production") {
62
63
 
63
64
  ## ShortCode Format
64
65
 
65
- | Prefix | Meaning |
66
- |--------|---------|
67
- | **M** | Mount index (multiple `.routes()` calls) |
68
- | **L** | Layout |
69
- | **C** | Cache boundary |
70
- | **R** | Route |
71
- | **P** | Parallel slot |
66
+ | Prefix | Meaning |
67
+ | ------ | ---------------------------------------- |
68
+ | **M** | Mount index (multiple `.routes()` calls) |
69
+ | **L** | Layout |
70
+ | **C** | Cache boundary |
71
+ | **R** | Route |
72
+ | **P** | Parallel slot |
72
73
 
73
74
  Example: `M0L0L1C0R0` = Mount 0 → Root Layout → Nested Layout → Cache → Route
74
75
 
@@ -85,7 +86,7 @@ Example: `M0L0L1C0R0` = Mount 0 → Root Layout → Nested Layout → Cache →
85
86
  import {
86
87
  serializeManifest,
87
88
  compareManifests,
88
- formatManifestDiff
89
+ formatManifestDiff,
89
90
  } from "@rangojs/router/__internal";
90
91
 
91
92
  const oldManifest = await router.debugManifest();
@@ -99,10 +100,13 @@ console.log(formatManifestDiff(diff));
99
100
  ## Common Issues
100
101
 
101
102
  ### Routes have `parentShortCode: null`
103
+
102
104
  Routes should have a layout parent. Check that `urls()` handler is being wrapped in root layout.
103
105
 
104
106
  ### Missing layouts in hierarchy
107
+
105
108
  Verify `layout()` calls wrap child routes correctly.
106
109
 
107
110
  ### Wrong mount index
111
+
108
112
  Multiple `.routes()` calls create separate mounts (M0, M1, etc.). Use `include()` to share context.
@@ -14,14 +14,14 @@ Configure document cache in router:
14
14
 
15
15
  ```typescript
16
16
  import { createRouter } from "@rangojs/router";
17
- import { CFCacheStore } from "@rangojs/router/cache/cf";
17
+ import { CFCacheStore } from "@rangojs/router/cache";
18
18
  import { urlpatterns } from "./urls";
19
19
 
20
- const router = createRouter<AppEnv>({
20
+ const router = createRouter<AppBindings>({
21
21
  document: Document,
22
22
  urls: urlpatterns,
23
- documentCache: (env) => ({
24
- store: new CFCacheStore({ ctx: env.ctx }),
23
+ documentCache: (_env, ctx) => ({
24
+ store: new CFCacheStore({ ctx: ctx! }),
25
25
  skipPaths: ["/api", "/admin"],
26
26
  debug: process.env.NODE_ENV === "development",
27
27
  }),
@@ -58,9 +58,9 @@ export const urlpatterns = urls(({ path, cache }) => [
58
58
  ```typescript
59
59
  createRouter({
60
60
  // ...
61
- documentCache: (env) => ({
61
+ documentCache: (_env, ctx) => ({
62
62
  // Cache store (required)
63
- store: new CFCacheStore({ ctx: env.ctx }),
63
+ store: new CFCacheStore({ ctx: ctx! }),
64
64
 
65
65
  // Skip specific paths
66
66
  skipPaths: ["/api", "/admin"],
@@ -102,6 +102,7 @@ Request → Check Cache
102
102
  ## Cache Status Header
103
103
 
104
104
  Response includes `x-document-cache-status`:
105
+
105
106
  - `HIT` - Fresh cache hit
106
107
  - `STALE` - Served stale, revalidating in background
107
108
  - `MISS` - Cache miss, response was generated fresh
@@ -109,6 +110,7 @@ Response includes `x-document-cache-status`:
109
110
  ## Cache Key Generation
110
111
 
111
112
  Default keys differentiate:
113
+
112
114
  - HTML requests: `{pathname}:html`
113
115
  - RSC partials: `{pathname}:{segmentHash}:rsc`
114
116
 
@@ -132,14 +134,14 @@ Segment hash ensures different cached responses for navigations from different s
132
134
  ```typescript
133
135
  // router.tsx
134
136
  import { createRouter } from "@rangojs/router";
135
- import { CFCacheStore } from "@rangojs/router/cache/cf";
137
+ import { CFCacheStore } from "@rangojs/router/cache";
136
138
  import { urlpatterns } from "./urls";
137
139
 
138
- const router = createRouter<AppEnv>({
140
+ const router = createRouter<AppBindings>({
139
141
  document: Document,
140
142
  urls: urlpatterns,
141
- documentCache: (env) => ({
142
- store: new CFCacheStore({ ctx: env.ctx }),
143
+ documentCache: (_env, ctx) => ({
144
+ store: new CFCacheStore({ ctx: ctx! }),
143
145
  skipPaths: ["/api"],
144
146
  debug: process.env.NODE_ENV === "development",
145
147
  }),
@@ -170,11 +172,11 @@ export const urlpatterns = urls(({ path, layout, cache, loader }) => [
170
172
 
171
173
  ## Document Cache vs Segment Cache
172
174
 
173
- | Feature | Document Cache | Segment Cache |
174
- |---------|---------------|---------------|
175
- | Granularity | Full response | Individual segments |
176
- | Opt-in | `documentCache` in cache() | `cache({ ttl, swr })` |
177
- | Use case | Static pages | Dynamic compositions |
178
- | Key includes | URL + segment hash | Route params |
175
+ | Feature | Document Cache | Segment Cache |
176
+ | ------------ | -------------------------- | --------------------- |
177
+ | Granularity | Full response | Individual segments |
178
+ | Opt-in | `documentCache` in cache() | `cache({ ttl, swr })` |
179
+ | Use case | Static pages | Dynamic compositions |
180
+ | Key includes | URL + segment hash | Route params |
179
181
 
180
182
  Use document cache for mostly-static pages. Use segment cache when different parts of a page have different cache requirements.
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: fonts
3
+ description: Load and configure web fonts with preload hints for optimal performance
4
+ argument-hint: [provider]
5
+ ---
6
+
7
+ # Fonts
8
+
9
+ Load web fonts in the Document component with `<link rel="preload">` for optimal performance. Fonts are declared in `<head>` alongside your stylesheet.
10
+
11
+ ## Google Fonts
12
+
13
+ ```tsx
14
+ // src/document.tsx
15
+ "use client";
16
+
17
+ import type { ReactNode } from "react";
18
+ import { MetaTags } from "@rangojs/router/client";
19
+ import styles from "./index.css?url";
20
+
21
+ export function Document({ children }: { children: ReactNode }) {
22
+ return (
23
+ <html lang="en">
24
+ <head>
25
+ {/* Preconnect to Google Fonts */}
26
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
27
+ <link
28
+ rel="preconnect"
29
+ href="https://fonts.gstatic.com"
30
+ crossOrigin="anonymous"
31
+ />
32
+
33
+ {/* Load font stylesheet */}
34
+ <link
35
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
36
+ rel="stylesheet"
37
+ />
38
+
39
+ {/* App styles */}
40
+ <link rel="preload" href={styles} as="style" />
41
+ <link rel="stylesheet" href={styles} />
42
+ <MetaTags />
43
+ </head>
44
+ <body>{children}</body>
45
+ </html>
46
+ );
47
+ }
48
+ ```
49
+
50
+ Then reference the font in CSS:
51
+
52
+ ```css
53
+ /* src/index.css */
54
+ body {
55
+ font-family: "Inter", sans-serif;
56
+ }
57
+ ```
58
+
59
+ Or with Tailwind (see `/tailwind`):
60
+
61
+ ```css
62
+ /* src/index.css */
63
+ @theme {
64
+ --font-sans: "Inter", sans-serif;
65
+ }
66
+ ```
67
+
68
+ ## Self-Hosted Fonts
69
+
70
+ Place font files in `public/fonts/` and use `@font-face`:
71
+
72
+ ```css
73
+ /* src/index.css */
74
+ @font-face {
75
+ font-family: "CustomFont";
76
+ src: url("/fonts/custom-regular.woff2") format("woff2");
77
+ font-weight: 400;
78
+ font-style: normal;
79
+ font-display: swap;
80
+ }
81
+
82
+ @font-face {
83
+ font-family: "CustomFont";
84
+ src: url("/fonts/custom-bold.woff2") format("woff2");
85
+ font-weight: 700;
86
+ font-style: normal;
87
+ font-display: swap;
88
+ }
89
+
90
+ body {
91
+ font-family: "CustomFont", sans-serif;
92
+ }
93
+ ```
94
+
95
+ Preload the most critical weight in the Document:
96
+
97
+ ```tsx
98
+ export function Document({ children }: { children: ReactNode }) {
99
+ return (
100
+ <html lang="en">
101
+ <head>
102
+ <link
103
+ rel="preload"
104
+ href="/fonts/custom-regular.woff2"
105
+ as="font"
106
+ type="font/woff2"
107
+ crossOrigin="anonymous"
108
+ />
109
+ <link rel="preload" href={styles} as="style" />
110
+ <link rel="stylesheet" href={styles} />
111
+ <MetaTags />
112
+ </head>
113
+ <body>{children}</body>
114
+ </html>
115
+ );
116
+ }
117
+ ```
118
+
119
+ ## Fontsource (Recommended for Vite)
120
+
121
+ `@fontsource-variable` packages are the recommended approach with Vite. Fonts are installed as npm dependencies, bundled by Vite, and served from your own domain -- no external requests, no privacy concerns, no FOUT from slow CDNs.
122
+
123
+ ```bash
124
+ pnpm add @fontsource-variable/inter
125
+ ```
126
+
127
+ Import the font CSS in your stylesheet. Vite resolves the `@fontsource-variable` import from `node_modules` and bundles the woff2 files as hashed assets automatically:
128
+
129
+ ```css
130
+ /* src/index.css */
131
+ @import "@fontsource-variable/inter";
132
+
133
+ body {
134
+ font-family: "Inter Variable", sans-serif;
135
+ }
136
+ ```
137
+
138
+ With Tailwind:
139
+
140
+ ```css
141
+ /* src/index.css */
142
+ @import "@fontsource-variable/inter";
143
+ @import "tailwindcss";
144
+
145
+ @theme {
146
+ --font-sans: "Inter Variable", sans-serif;
147
+ }
148
+ ```
149
+
150
+ Why this is preferred over Google Fonts with Vite:
151
+
152
+ - No external network requests at runtime -- fonts are bundled into your build output
153
+ - No `<link rel="preconnect">` or extra stylesheet needed in the Document
154
+ - Variable font = single file covers all weights, smaller total download
155
+ - Vite handles cache-busting via content hashes
156
+ - Works offline and in edge deployments (Cloudflare Workers) without external dependencies
157
+
158
+ Browse available fonts at fontsource.org. Use `@fontsource-variable/*` for variable fonts and `@fontsource/*` for static fonts.
159
+
160
+ ## Performance Tips
161
+
162
+ - Prefer `@fontsource-variable` with Vite for self-hosted, zero-config font loading
163
+ - Use `font-display: swap` to prevent invisible text during font load
164
+ - Preload only the most critical font weight (usually regular 400)
165
+ - Prefer `woff2` format for smaller file sizes
166
+ - Use variable fonts when multiple weights are needed to reduce total file count
167
+ - `<link rel="preconnect">` eliminates DNS + TLS latency for external font providers