@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.8a4d0430

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 (300) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +4474 -863
  5. package/package.json +60 -51
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +50 -21
  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 +89 -30
  18. package/skills/loader/SKILL.md +388 -38
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +78 -1
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +226 -14
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +318 -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/event-controller.ts +87 -64
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/intercept-utils.ts +52 -0
  38. package/src/browser/link-interceptor.ts +24 -4
  39. package/src/browser/logging.ts +55 -0
  40. package/src/browser/merge-segment-loaders.ts +20 -12
  41. package/src/browser/navigation-bridge.ts +285 -553
  42. package/src/browser/navigation-client.ts +124 -71
  43. package/src/browser/navigation-store.ts +33 -50
  44. package/src/browser/navigation-transaction.ts +295 -0
  45. package/src/browser/network-error-handler.ts +61 -0
  46. package/src/browser/partial-update.ts +258 -308
  47. package/src/browser/prefetch/cache.ts +146 -0
  48. package/src/browser/prefetch/fetch.ts +135 -0
  49. package/src/browser/prefetch/observer.ts +65 -0
  50. package/src/browser/prefetch/policy.ts +42 -0
  51. package/src/browser/prefetch/queue.ts +88 -0
  52. package/src/browser/rango-state.ts +112 -0
  53. package/src/browser/react/Link.tsx +185 -73
  54. package/src/browser/react/NavigationProvider.tsx +51 -11
  55. package/src/browser/react/context.ts +6 -0
  56. package/src/browser/react/filter-segment-order.ts +11 -0
  57. package/src/browser/react/index.ts +12 -12
  58. package/src/browser/react/location-state-shared.ts +95 -53
  59. package/src/browser/react/location-state.ts +60 -15
  60. package/src/browser/react/mount-context.ts +6 -1
  61. package/src/browser/react/nonce-context.ts +23 -0
  62. package/src/browser/react/shallow-equal.ts +27 -0
  63. package/src/browser/react/use-action.ts +29 -51
  64. package/src/browser/react/use-client-cache.ts +5 -3
  65. package/src/browser/react/use-handle.ts +32 -79
  66. package/src/browser/react/use-href.tsx +2 -2
  67. package/src/browser/react/use-link-status.ts +6 -5
  68. package/src/browser/react/use-navigation.ts +22 -63
  69. package/src/browser/react/use-params.ts +65 -0
  70. package/src/browser/react/use-pathname.ts +47 -0
  71. package/src/browser/react/use-router.ts +63 -0
  72. package/src/browser/react/use-search-params.ts +56 -0
  73. package/src/browser/react/use-segments.ts +80 -97
  74. package/src/browser/response-adapter.ts +73 -0
  75. package/src/browser/rsc-router.tsx +107 -26
  76. package/src/browser/scroll-restoration.ts +92 -16
  77. package/src/browser/segment-reconciler.ts +216 -0
  78. package/src/browser/segment-structure-assert.ts +16 -0
  79. package/src/browser/server-action-bridge.ts +504 -599
  80. package/src/browser/shallow.ts +6 -1
  81. package/src/browser/types.ts +109 -47
  82. package/src/browser/validate-redirect-origin.ts +29 -0
  83. package/src/build/generate-manifest.ts +235 -24
  84. package/src/build/generate-route-types.ts +36 -0
  85. package/src/build/index.ts +13 -0
  86. package/src/build/route-trie.ts +265 -0
  87. package/src/build/route-types/ast-helpers.ts +25 -0
  88. package/src/build/route-types/ast-route-extraction.ts +98 -0
  89. package/src/build/route-types/codegen.ts +102 -0
  90. package/src/build/route-types/include-resolution.ts +411 -0
  91. package/src/build/route-types/param-extraction.ts +48 -0
  92. package/src/build/route-types/per-module-writer.ts +128 -0
  93. package/src/build/route-types/router-processing.ts +469 -0
  94. package/src/build/route-types/scan-filter.ts +78 -0
  95. package/src/build/runtime-discovery.ts +231 -0
  96. package/src/cache/background-task.ts +34 -0
  97. package/src/cache/cache-key-utils.ts +44 -0
  98. package/src/cache/cache-policy.ts +125 -0
  99. package/src/cache/cache-runtime.ts +338 -0
  100. package/src/cache/cache-scope.ts +120 -303
  101. package/src/cache/cf/cf-cache-store.ts +119 -7
  102. package/src/cache/cf/index.ts +8 -2
  103. package/src/cache/document-cache.ts +101 -72
  104. package/src/cache/handle-capture.ts +81 -0
  105. package/src/cache/handle-snapshot.ts +41 -0
  106. package/src/cache/index.ts +0 -15
  107. package/src/cache/memory-segment-store.ts +191 -13
  108. package/src/cache/profile-registry.ts +73 -0
  109. package/src/cache/read-through-swr.ts +134 -0
  110. package/src/cache/segment-codec.ts +256 -0
  111. package/src/cache/taint.ts +98 -0
  112. package/src/cache/types.ts +72 -122
  113. package/src/client.rsc.tsx +3 -1
  114. package/src/client.tsx +106 -126
  115. package/src/component-utils.ts +4 -4
  116. package/src/components/DefaultDocument.tsx +5 -1
  117. package/src/context-var.ts +86 -0
  118. package/src/debug.ts +17 -7
  119. package/src/errors.ts +108 -2
  120. package/src/handle.ts +15 -29
  121. package/src/handles/MetaTags.tsx +73 -20
  122. package/src/handles/breadcrumbs.ts +66 -0
  123. package/src/handles/index.ts +1 -0
  124. package/src/handles/meta.ts +30 -13
  125. package/src/host/cookie-handler.ts +21 -15
  126. package/src/host/errors.ts +8 -8
  127. package/src/host/index.ts +4 -7
  128. package/src/host/pattern-matcher.ts +27 -27
  129. package/src/host/router.ts +61 -39
  130. package/src/host/testing.ts +8 -8
  131. package/src/host/types.ts +15 -7
  132. package/src/host/utils.ts +1 -1
  133. package/src/href-client.ts +119 -29
  134. package/src/index.rsc.ts +153 -19
  135. package/src/index.ts +211 -30
  136. package/src/internal-debug.ts +11 -0
  137. package/src/loader.rsc.ts +26 -157
  138. package/src/loader.ts +27 -10
  139. package/src/network-error-thrower.tsx +3 -1
  140. package/src/outlet-provider.tsx +45 -0
  141. package/src/prerender/param-hash.ts +37 -0
  142. package/src/prerender/store.ts +185 -0
  143. package/src/prerender.ts +463 -0
  144. package/src/reverse.ts +330 -0
  145. package/src/root-error-boundary.tsx +41 -29
  146. package/src/route-content-wrapper.tsx +7 -4
  147. package/src/route-definition/dsl-helpers.ts +934 -0
  148. package/src/route-definition/helper-factories.ts +200 -0
  149. package/src/route-definition/helpers-types.ts +430 -0
  150. package/src/route-definition/index.ts +52 -0
  151. package/src/route-definition/redirect.ts +93 -0
  152. package/src/route-definition.ts +1 -1428
  153. package/src/route-map-builder.ts +211 -123
  154. package/src/route-name.ts +53 -0
  155. package/src/route-types.ts +59 -8
  156. package/src/router/content-negotiation.ts +116 -0
  157. package/src/router/debug-manifest.ts +72 -0
  158. package/src/router/error-handling.ts +9 -9
  159. package/src/router/find-match.ts +158 -0
  160. package/src/router/handler-context.ts +374 -81
  161. package/src/router/intercept-resolution.ts +395 -0
  162. package/src/router/lazy-includes.ts +234 -0
  163. package/src/router/loader-resolution.ts +215 -122
  164. package/src/router/logging.ts +248 -0
  165. package/src/router/manifest.ts +148 -35
  166. package/src/router/match-api.ts +620 -0
  167. package/src/router/match-context.ts +5 -3
  168. package/src/router/match-handlers.ts +440 -0
  169. package/src/router/match-middleware/background-revalidation.ts +80 -93
  170. package/src/router/match-middleware/cache-lookup.ts +382 -9
  171. package/src/router/match-middleware/cache-store.ts +51 -22
  172. package/src/router/match-middleware/intercept-resolution.ts +55 -17
  173. package/src/router/match-middleware/segment-resolution.ts +24 -6
  174. package/src/router/match-pipelines.ts +10 -45
  175. package/src/router/match-result.ts +34 -28
  176. package/src/router/metrics.ts +235 -15
  177. package/src/router/middleware-cookies.ts +55 -0
  178. package/src/router/middleware-types.ts +222 -0
  179. package/src/router/middleware.ts +324 -367
  180. package/src/router/pattern-matching.ts +211 -43
  181. package/src/router/prerender-match.ts +402 -0
  182. package/src/router/preview-match.ts +170 -0
  183. package/src/router/revalidation.ts +137 -38
  184. package/src/router/router-context.ts +36 -21
  185. package/src/router/router-interfaces.ts +452 -0
  186. package/src/router/router-options.ts +592 -0
  187. package/src/router/router-registry.ts +24 -0
  188. package/src/router/segment-resolution/fresh.ts +570 -0
  189. package/src/router/segment-resolution/helpers.ts +263 -0
  190. package/src/router/segment-resolution/loader-cache.ts +198 -0
  191. package/src/router/segment-resolution/revalidation.ts +1241 -0
  192. package/src/router/segment-resolution/static-store.ts +67 -0
  193. package/src/router/segment-resolution.ts +21 -0
  194. package/src/router/segment-wrappers.ts +289 -0
  195. package/src/router/telemetry-otel.ts +299 -0
  196. package/src/router/telemetry.ts +300 -0
  197. package/src/router/timeout.ts +148 -0
  198. package/src/router/trie-matching.ts +239 -0
  199. package/src/router/types.ts +77 -3
  200. package/src/router.ts +692 -4257
  201. package/src/rsc/handler-context.ts +45 -0
  202. package/src/rsc/handler.ts +764 -754
  203. package/src/rsc/helpers.ts +140 -6
  204. package/src/rsc/index.ts +0 -20
  205. package/src/rsc/loader-fetch.ts +209 -0
  206. package/src/rsc/manifest-init.ts +86 -0
  207. package/src/rsc/nonce.ts +14 -0
  208. package/src/rsc/origin-guard.ts +141 -0
  209. package/src/rsc/progressive-enhancement.ts +379 -0
  210. package/src/rsc/response-error.ts +37 -0
  211. package/src/rsc/response-route-handler.ts +347 -0
  212. package/src/rsc/rsc-rendering.ts +235 -0
  213. package/src/rsc/runtime-warnings.ts +42 -0
  214. package/src/rsc/server-action.ts +348 -0
  215. package/src/rsc/ssr-setup.ts +128 -0
  216. package/src/rsc/types.ts +38 -11
  217. package/src/search-params.ts +230 -0
  218. package/src/segment-system.tsx +25 -13
  219. package/src/server/context.ts +182 -51
  220. package/src/server/cookie-store.ts +190 -0
  221. package/src/server/fetchable-loader-store.ts +37 -0
  222. package/src/server/handle-store.ts +94 -15
  223. package/src/server/loader-registry.ts +15 -56
  224. package/src/server/request-context.ts +430 -70
  225. package/src/server.ts +35 -130
  226. package/src/ssr/index.tsx +100 -31
  227. package/src/static-handler.ts +114 -0
  228. package/src/theme/ThemeProvider.tsx +21 -15
  229. package/src/theme/ThemeScript.tsx +5 -5
  230. package/src/theme/constants.ts +5 -2
  231. package/src/theme/index.ts +4 -14
  232. package/src/theme/theme-context.ts +4 -30
  233. package/src/theme/theme-script.ts +21 -18
  234. package/src/types/boundaries.ts +158 -0
  235. package/src/types/cache-types.ts +198 -0
  236. package/src/types/error-types.ts +192 -0
  237. package/src/types/global-namespace.ts +100 -0
  238. package/src/types/handler-context.ts +687 -0
  239. package/src/types/index.ts +88 -0
  240. package/src/types/loader-types.ts +183 -0
  241. package/src/types/route-config.ts +170 -0
  242. package/src/types/route-entry.ts +102 -0
  243. package/src/types/segments.ts +148 -0
  244. package/src/types.ts +1 -1623
  245. package/src/urls/include-helper.ts +197 -0
  246. package/src/urls/index.ts +53 -0
  247. package/src/urls/path-helper-types.ts +339 -0
  248. package/src/urls/path-helper.ts +329 -0
  249. package/src/urls/pattern-types.ts +95 -0
  250. package/src/urls/response-types.ts +106 -0
  251. package/src/urls/type-extraction.ts +372 -0
  252. package/src/urls/urls-function.ts +98 -0
  253. package/src/urls.ts +1 -802
  254. package/src/use-loader.tsx +85 -77
  255. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  256. package/src/vite/discovery/discover-routers.ts +344 -0
  257. package/src/vite/discovery/prerender-collection.ts +385 -0
  258. package/src/vite/discovery/route-types-writer.ts +258 -0
  259. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  260. package/src/vite/discovery/state.ts +110 -0
  261. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  262. package/src/vite/index.ts +11 -1129
  263. package/src/vite/plugin-types.ts +131 -0
  264. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  265. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  266. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  267. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
  268. package/src/vite/plugins/expose-id-utils.ts +287 -0
  269. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  270. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  271. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  272. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  273. package/src/vite/plugins/expose-ids/types.ts +45 -0
  274. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  275. package/src/vite/plugins/refresh-cmd.ts +65 -0
  276. package/src/vite/plugins/use-cache-transform.ts +323 -0
  277. package/src/vite/plugins/version-injector.ts +83 -0
  278. package/src/vite/plugins/version-plugin.ts +254 -0
  279. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  280. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  281. package/src/vite/rango.ts +510 -0
  282. package/src/vite/router-discovery.ts +785 -0
  283. package/src/vite/utils/ast-handler-extract.ts +517 -0
  284. package/src/vite/utils/banner.ts +36 -0
  285. package/src/vite/utils/bundle-analysis.ts +137 -0
  286. package/src/vite/utils/manifest-utils.ts +70 -0
  287. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  288. package/src/vite/utils/prerender-utils.ts +189 -0
  289. package/src/vite/utils/shared-utils.ts +169 -0
  290. package/CLAUDE.md +0 -43
  291. package/src/browser/lru-cache.ts +0 -69
  292. package/src/browser/request-controller.ts +0 -164
  293. package/src/cache/memory-store.ts +0 -253
  294. package/src/href-context.ts +0 -33
  295. package/src/href.ts +0 -255
  296. package/src/server/route-manifest-cache.ts +0 -173
  297. package/src/vite/expose-handle-id.ts +0 -209
  298. package/src/vite/expose-loader-id.ts +0 -426
  299. package/src/vite/expose-location-state-id.ts +0 -177
  300. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -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