@timber-js/app 0.2.0-alpha.196 → 0.2.0-alpha.197

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 (285) hide show
  1. package/dist/_chunks/{actions-CWYtq6ii.js → actions-BS-m5SLv.js} +3 -3
  2. package/dist/_chunks/{actions-CWYtq6ii.js.map → actions-BS-m5SLv.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
  4. package/dist/_chunks/{build-manifest-DWppEdLB.js → build-manifest-DTmSGLRz.js} +51 -2
  5. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +1 -0
  6. package/dist/_chunks/{cache-api-CQeYzA5g.js → cache-api-DqzgTEqk.js} +4 -49
  7. package/dist/_chunks/cache-api-DqzgTEqk.js.map +1 -0
  8. package/dist/_chunks/{chains-h7EO-u3n.js → chains-CZG7E5zg.js} +2 -2
  9. package/dist/_chunks/{chains-h7EO-u3n.js.map → chains-CZG7E5zg.js.map} +1 -1
  10. package/dist/_chunks/{cli-check-BVthpfLS.js → cli-check-bE3H5Bjr.js} +3 -3
  11. package/dist/_chunks/{cli-check-BVthpfLS.js.map → cli-check-bE3H5Bjr.js.map} +1 -1
  12. package/dist/_chunks/{cli-schema-sync-3Wutm8pH.js → cli-schema-sync-DTy_-Msq.js} +2 -2
  13. package/dist/_chunks/{cli-schema-sync-3Wutm8pH.js.map → cli-schema-sync-DTy_-Msq.js.map} +1 -1
  14. package/dist/_chunks/{cloudflare-BKJC3SC_.js → cloudflare-BFb__LYG.js} +2 -2
  15. package/dist/_chunks/{cloudflare-BKJC3SC_.js.map → cloudflare-BFb__LYG.js.map} +1 -1
  16. package/dist/_chunks/{convention-lint-DO10_pVl.js → convention-lint-n3RJLgww.js} +2 -2
  17. package/dist/_chunks/{convention-lint-DO10_pVl.js.map → convention-lint-n3RJLgww.js.map} +1 -1
  18. package/dist/_chunks/{error-boundary-D-lkwyaD.js → error-boundary-BvRCCmbN.js} +3 -3
  19. package/dist/_chunks/{error-boundary-D-lkwyaD.js.map → error-boundary-BvRCCmbN.js.map} +1 -1
  20. package/dist/_chunks/{href-validation-CMc5JRls.js → href-validation-BIrxavIy.js} +74 -2
  21. package/dist/_chunks/href-validation-BIrxavIy.js.map +1 -0
  22. package/dist/_chunks/{live-graph-Bx4HodF1.js → live-graph-BXDsdzBv.js} +3 -3
  23. package/dist/_chunks/{live-graph-Bx4HodF1.js.map → live-graph-BXDsdzBv.js.map} +1 -1
  24. package/dist/_chunks/{logger-pumCm3Il.js → logger-DDirEsn7.js} +3 -4
  25. package/dist/_chunks/{logger-pumCm3Il.js.map → logger-DDirEsn7.js.map} +1 -1
  26. package/dist/_chunks/navigation-root-B00jjGd5.js +233 -0
  27. package/dist/_chunks/navigation-root-B00jjGd5.js.map +1 -0
  28. package/dist/_chunks/{segment-context-CjOlyB8Y.js → param-value-C8TNYchQ.js} +2 -33
  29. package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
  30. package/dist/_chunks/{poison-scan-BAxfTT5L.js → poison-scan-BoDLgbix.js} +2 -2
  31. package/dist/_chunks/{poison-scan-BAxfTT5L.js.map → poison-scan-BoDLgbix.js.map} +1 -1
  32. package/dist/_chunks/{router-ref-BzqbPwYC.js → router-ref-8gr8qsxN.js} +2 -2
  33. package/dist/_chunks/{router-ref-BzqbPwYC.js.map → router-ref-8gr8qsxN.js.map} +1 -1
  34. package/dist/_chunks/{rsc-cache-key-DD0fl_-s.js → rsc-cache-key-ClUiXQnK.js} +2 -2
  35. package/dist/_chunks/{rsc-cache-key-DD0fl_-s.js.map → rsc-cache-key-ClUiXQnK.js.map} +1 -1
  36. package/dist/_chunks/{scanner-BRIOmHE2.js → scanner-tdFPvDYi.js} +174 -7
  37. package/dist/_chunks/scanner-tdFPvDYi.js.map +1 -0
  38. package/dist/_chunks/segment-context-D9_89u34.js +34 -0
  39. package/dist/_chunks/segment-context-D9_89u34.js.map +1 -0
  40. package/dist/_chunks/singleflight-2lUWfcAk.js +54 -0
  41. package/dist/_chunks/singleflight-2lUWfcAk.js.map +1 -0
  42. package/dist/_chunks/{ssr-data-Ya2HJPFp.js → ssr-data-BQGhTPAK.js} +2 -17
  43. package/dist/_chunks/ssr-data-BQGhTPAK.js.map +1 -0
  44. package/dist/_chunks/{walkers-BU6z9xRV.js → walkers-DNX05dC0.js} +2 -2
  45. package/dist/_chunks/{walkers-BU6z9xRV.js.map → walkers-DNX05dC0.js.map} +1 -1
  46. package/dist/adapters/cloudflare-dev.js +1 -1
  47. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  48. package/dist/adapters/cloudflare.js +1 -1
  49. package/dist/adapters/nitro.d.ts +1 -1
  50. package/dist/adapters/nitro.d.ts.map +1 -1
  51. package/dist/adapters/nitro.js.map +1 -1
  52. package/dist/analyze/crawl-entry.js +2 -2
  53. package/dist/analyze/graph-command.js +2 -2
  54. package/dist/cache/index.js +1 -1
  55. package/dist/cache/singleflight.d.ts +2 -0
  56. package/dist/cache/singleflight.d.ts.map +1 -1
  57. package/dist/cli.js +2 -2
  58. package/dist/client/browser-entry/hydrate.d.ts +21 -15
  59. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  60. package/dist/client/browser-entry/index.d.ts +4 -3
  61. package/dist/client/browser-entry/index.d.ts.map +1 -1
  62. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  63. package/dist/client/browser-entry/router-init.d.ts +17 -1
  64. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  65. package/dist/client/error-boundary.js +1 -1
  66. package/dist/client/global-context.d.ts +15 -0
  67. package/dist/client/global-context.d.ts.map +1 -0
  68. package/dist/client/index.js +138 -35
  69. package/dist/client/index.js.map +1 -1
  70. package/dist/client/internal.d.ts +0 -1
  71. package/dist/client/internal.d.ts.map +1 -1
  72. package/dist/client/internal.js +206 -55
  73. package/dist/client/internal.js.map +1 -1
  74. package/dist/client/link.d.ts.map +1 -1
  75. package/dist/client/location-search.d.ts +12 -0
  76. package/dist/client/location-search.d.ts.map +1 -0
  77. package/dist/client/navigation-api.d.ts.map +1 -1
  78. package/dist/client/navigation-commit.d.ts +18 -0
  79. package/dist/client/navigation-commit.d.ts.map +1 -1
  80. package/dist/client/navigation-context.d.ts +13 -11
  81. package/dist/client/navigation-context.d.ts.map +1 -1
  82. package/dist/client/navigation-root.d.ts +47 -108
  83. package/dist/client/navigation-root.d.ts.map +1 -1
  84. package/dist/client/navigation-transition.d.ts +136 -0
  85. package/dist/client/navigation-transition.d.ts.map +1 -0
  86. package/dist/client/nuqs-adapter.d.ts.map +1 -1
  87. package/dist/client/params-context.d.ts +4 -5
  88. package/dist/client/params-context.d.ts.map +1 -1
  89. package/dist/client/react-root.d.ts +44 -0
  90. package/dist/client/react-root.d.ts.map +1 -0
  91. package/dist/client/router-pipeline.d.ts +2 -2
  92. package/dist/client/router-pipeline.d.ts.map +1 -1
  93. package/dist/client/router-types.d.ts +12 -2
  94. package/dist/client/router-types.d.ts.map +1 -1
  95. package/dist/client/router.d.ts.map +1 -1
  96. package/dist/client/segment-cache.d.ts +39 -0
  97. package/dist/client/segment-cache.d.ts.map +1 -1
  98. package/dist/client/segment-context.d.ts.map +1 -1
  99. package/dist/client/segment-outlet.d.ts +25 -14
  100. package/dist/client/segment-outlet.d.ts.map +1 -1
  101. package/dist/client/segment-update-context.d.ts +3 -9
  102. package/dist/client/segment-update-context.d.ts.map +1 -1
  103. package/dist/client/slot-content-cache-context.d.ts +35 -0
  104. package/dist/client/slot-content-cache-context.d.ts.map +1 -0
  105. package/dist/client/ssr-data.d.ts +8 -2
  106. package/dist/client/ssr-data.d.ts.map +1 -1
  107. package/dist/client/state.d.ts +0 -15
  108. package/dist/client/state.d.ts.map +1 -1
  109. package/dist/client/use-pathname.d.ts +13 -11
  110. package/dist/client/use-pathname.d.ts.map +1 -1
  111. package/dist/client/use-search-params.d.ts +13 -13
  112. package/dist/client/use-search-params.d.ts.map +1 -1
  113. package/dist/client/use-segment-params.d.ts +18 -68
  114. package/dist/client/use-segment-params.d.ts.map +1 -1
  115. package/dist/config-types.d.ts +17 -0
  116. package/dist/config-types.d.ts.map +1 -1
  117. package/dist/config-validation.d.ts.map +1 -1
  118. package/dist/cookies/index.js +1 -1
  119. package/dist/dev-tools/holding-server.d.ts +15 -10
  120. package/dist/dev-tools/holding-server.d.ts.map +1 -1
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +44 -43
  123. package/dist/index.js.map +1 -1
  124. package/dist/plugins/dev-server.d.ts.map +1 -1
  125. package/dist/plugins/entries.d.ts.map +1 -1
  126. package/dist/plugins/shims.d.ts.map +1 -1
  127. package/dist/plugins/static-build.d.ts +2 -2
  128. package/dist/plugins/static-build.d.ts.map +1 -1
  129. package/dist/routing/index.js +2 -2
  130. package/dist/routing/interception-overlap.d.ts +35 -0
  131. package/dist/routing/interception-overlap.d.ts.map +1 -0
  132. package/dist/routing/interception.d.ts.map +1 -1
  133. package/dist/rsc-runtime/ssr.d.ts +3 -1
  134. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  135. package/dist/server/als-registry.d.ts +6 -0
  136. package/dist/server/als-registry.d.ts.map +1 -1
  137. package/dist/server/csp-nonce.d.ts +45 -0
  138. package/dist/server/csp-nonce.d.ts.map +1 -0
  139. package/dist/server/default-status-page.d.ts.map +1 -1
  140. package/dist/server/deny-renderer.d.ts.map +1 -1
  141. package/dist/server/flight-scripts.d.ts +5 -2
  142. package/dist/server/flight-scripts.d.ts.map +1 -1
  143. package/dist/server/html-injector-core.d.ts +17 -2
  144. package/dist/server/html-injector-core.d.ts.map +1 -1
  145. package/dist/server/html-injectors.d.ts +3 -2
  146. package/dist/server/html-injectors.d.ts.map +1 -1
  147. package/dist/server/index.js +2 -2
  148. package/dist/server/internal.js +86 -37
  149. package/dist/server/internal.js.map +1 -1
  150. package/dist/server/metadata-render.d.ts.map +1 -1
  151. package/dist/server/node-stream-transforms.d.ts +3 -17
  152. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  153. package/dist/server/nuqs-ssr-provider.d.ts +7 -3
  154. package/dist/server/nuqs-ssr-provider.d.ts.map +1 -1
  155. package/dist/server/pipeline-phases.d.ts.map +1 -1
  156. package/dist/server/prebuilt/key-discipline.d.ts +32 -3
  157. package/dist/server/prebuilt/key-discipline.d.ts.map +1 -1
  158. package/dist/server/primitives.d.ts.map +1 -1
  159. package/dist/server/render-utils.d.ts +4 -3
  160. package/dist/server/render-utils.d.ts.map +1 -1
  161. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
  162. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  163. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  164. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  165. package/dist/server/ssr-bridge-types.d.ts +22 -2
  166. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  167. package/dist/server/ssr-entry.d.ts.map +1 -1
  168. package/dist/server/ssr-render.d.ts +5 -1
  169. package/dist/server/ssr-render.d.ts.map +1 -1
  170. package/dist/server/ssr-wrappers.d.ts +59 -27
  171. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  172. package/dist/server/types.d.ts +10 -0
  173. package/dist/server/types.d.ts.map +1 -1
  174. package/dist/shims/navigation-rsc.d.ts +21 -0
  175. package/dist/shims/navigation-rsc.d.ts.map +1 -0
  176. package/docs/api/30-api-server.mdx +1 -0
  177. package/docs/api/35-api-typescript.mdx +4 -83
  178. package/docs/learn/03-fetching-data.mdx +1 -1
  179. package/docs/learn/{03b-access-control.mdx → 04-access-control.mdx} +2 -17
  180. package/docs/learn/05-the-flush-point.mdx +175 -0
  181. package/docs/learn/{05-typed-params.mdx → 06-typed-params.mdx} +1 -1
  182. package/docs/learn/07-typed-routes.mdx +25 -49
  183. package/docs/learn/{08-streaming.mdx → 09-streaming.mdx} +1 -7
  184. package/docs/learn/{10-middleware.mdx → 11-middleware.mdx} +1 -0
  185. package/package.json +3 -3
  186. package/src/adapters/nitro.ts +7 -7
  187. package/src/cache/singleflight.ts +5 -0
  188. package/src/client/browser-entry/hydrate.ts +54 -104
  189. package/src/client/browser-entry/index.ts +16 -6
  190. package/src/client/browser-entry/post-hydration.ts +3 -2
  191. package/src/client/browser-entry/router-init.ts +84 -33
  192. package/src/client/global-context.ts +31 -0
  193. package/src/client/internal.ts +1 -2
  194. package/src/client/link.tsx +18 -18
  195. package/src/client/location-search.ts +15 -0
  196. package/src/client/navigation-api.ts +4 -2
  197. package/src/client/navigation-commit.ts +48 -2
  198. package/src/client/navigation-context.ts +25 -37
  199. package/src/client/navigation-root.tsx +55 -411
  200. package/src/client/navigation-transition.ts +278 -0
  201. package/src/client/nuqs-adapter.tsx +4 -5
  202. package/src/client/params-context.ts +13 -18
  203. package/src/client/react-root.ts +72 -0
  204. package/src/client/router-lifecycle.ts +1 -1
  205. package/src/client/router-pipeline.ts +96 -22
  206. package/src/client/router-types.ts +12 -2
  207. package/src/client/router.ts +48 -36
  208. package/src/client/segment-cache.ts +70 -2
  209. package/src/client/segment-context.ts +7 -4
  210. package/src/client/segment-outlet.tsx +41 -86
  211. package/src/client/segment-update-context.ts +7 -26
  212. package/src/client/slot-content-cache-context.ts +43 -0
  213. package/src/client/ssr-data.ts +8 -2
  214. package/src/client/state.ts +0 -26
  215. package/src/client/use-pathname.ts +21 -31
  216. package/src/client/use-search-params.ts +31 -29
  217. package/src/client/use-segment-params.ts +27 -126
  218. package/src/config-types.ts +17 -0
  219. package/src/config-validation.ts +17 -0
  220. package/src/dev-tools/holding-server.ts +23 -12
  221. package/src/index.ts +26 -11
  222. package/src/plugins/dev-server.ts +9 -12
  223. package/src/plugins/entries.ts +3 -0
  224. package/src/plugins/shims.ts +8 -7
  225. package/src/plugins/static-build.ts +9 -5
  226. package/src/routing/interception-overlap.ts +141 -0
  227. package/src/routing/interception.ts +118 -5
  228. package/src/rsc-runtime/ssr.ts +3 -2
  229. package/src/server/als-registry.ts +6 -0
  230. package/src/server/csp-nonce.ts +70 -0
  231. package/src/server/default-status-page.ts +1 -0
  232. package/src/server/deny-renderer.ts +7 -3
  233. package/src/server/flight-scripts.ts +9 -4
  234. package/src/server/html-injector-core.ts +26 -9
  235. package/src/server/html-injectors.ts +8 -8
  236. package/src/server/metadata-render.ts +26 -4
  237. package/src/server/node-stream-transforms.ts +7 -20
  238. package/src/server/nuqs-ssr-provider.tsx +8 -7
  239. package/src/server/pipeline-phases.ts +5 -0
  240. package/src/server/prebuilt/key-discipline.ts +82 -13
  241. package/src/server/prebuilt-runtime.ts +2 -2
  242. package/src/server/primitives.ts +4 -4
  243. package/src/server/render-utils.ts +8 -4
  244. package/src/server/rsc-entry/action-middleware-runner.ts +7 -0
  245. package/src/server/rsc-entry/error-renderer.ts +5 -2
  246. package/src/server/rsc-entry/index.ts +8 -0
  247. package/src/server/rsc-entry/ssr-renderer.ts +11 -4
  248. package/src/server/ssr-bridge-types.ts +22 -2
  249. package/src/server/ssr-entry.ts +35 -28
  250. package/src/server/ssr-render.ts +13 -4
  251. package/src/server/ssr-wrappers.tsx +81 -61
  252. package/src/server/types.ts +10 -0
  253. package/src/shared/slot-params.ts +3 -4
  254. package/src/shims/navigation-rsc.ts +47 -0
  255. package/dist/_chunks/build-manifest-DWppEdLB.js.map +0 -1
  256. package/dist/_chunks/cache-api-CQeYzA5g.js.map +0 -1
  257. package/dist/_chunks/href-validation-CMc5JRls.js.map +0 -1
  258. package/dist/_chunks/scanner-BRIOmHE2.js.map +0 -1
  259. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +0 -1
  260. package/dist/_chunks/slot-params-BCTmZkQB.js +0 -76
  261. package/dist/_chunks/slot-params-BCTmZkQB.js.map +0 -1
  262. package/dist/_chunks/ssr-data-Ya2HJPFp.js.map +0 -1
  263. package/dist/_chunks/use-segment-params-DzTBpkvj.js +0 -398
  264. package/dist/_chunks/use-segment-params-DzTBpkvj.js.map +0 -1
  265. package/docs/learn/04-loading-states.mdx +0 -67
  266. package/docs/learn/04b-the-flush-point.mdx +0 -115
  267. package/docs/learn/12-client-navigation.mdx +0 -176
  268. package/docs/learn/13-configuration.mdx +0 -166
  269. package/docs/more/01-advanced-routing.mdx +0 -344
  270. package/docs/more/02-advanced-forms.mdx +0 -137
  271. package/docs/more/03-coming-from-nextjs.mdx +0 -186
  272. package/docs/more/04-metadata-and-fonts.mdx +0 -193
  273. package/docs/more/04b-mdx.mdx +0 -229
  274. package/docs/more/05-content-collections.mdx +0 -90
  275. package/docs/more/06-instrumentation.mdx +0 -214
  276. package/docs/more/07-security.mdx +0 -129
  277. package/docs/more/08-developer-experience.mdx +0 -134
  278. package/docs/more/40-why-timber.mdx +0 -50
  279. package/docs/more/41-timber-vs-nextjs.mdx +0 -81
  280. package/docs/more/42-timber-vs-others.mdx +0 -68
  281. package/docs/more/50-ai-agent-instructions.mdx +0 -171
  282. /package/docs/learn/{06-forms-and-actions.mdx → 08-forms-and-actions.mdx} +0 -0
  283. /package/docs/learn/{09-caching.mdx → 10-caching.mdx} +0 -0
  284. /package/docs/learn/{11-error-handling.mdx → 12-error-handling.mdx} +0 -0
  285. /package/docs/learn/{14-deploying.mdx → 13-deploying.mdx} +0 -0
@@ -1,115 +0,0 @@
1
- ---
2
- title: 'The Flush Point'
3
- description: 'When timber commits the HTTP status code and why it matters.'
4
- slug: 'the-flush-point'
5
- # notAI: true
6
- ---
7
-
8
- # The Flush Point
9
-
10
- The flush point is when the framework sends the first byte of HTML to the browser and commits the HTTP status code. In a traditional PHP website, this happens all at once.
11
-
12
- But modern web frameworks, and notably react server components, have embraced a streaming architecture. Allowing you to send content _after_ the flush point.
13
-
14
- While this is ultimately incredibly powerful, it brings forth confusion because contextually, code can have different side effects depending on _when_ you call it.
15
-
16
- On a next.js app, if I call `notFound()` inside of a `page.tsx` that has a sibling `loading.tsx` – my status code will return `200`. By obfuscating the flush point, we've actually just made developer clarity murky.
17
-
18
- So timber works differently. At no point does timber flush early, unless _you_ (the developer), choose to place a `<Suspense>` boundary. Often this will end up being below your `page.tsx` level, so you'll still have access to controlling and sending proper status codes.
19
-
20
- ------
21
-
22
- AI below..
23
-
24
- ## The Problem
25
-
26
- Most streaming frameworks send a `200 OK` immediately and figure out the real outcome later:
27
-
28
- ```
29
- Request → 200 OK → ... render ... → oh, it's a 404
30
- ```
31
-
32
- By the time the server discovers the page doesn't exist, the status code is already sent. The client sees `200`. Search engines see `200`. CDNs cache it as `200`.
33
-
34
- This breaks search engines (deleted pages never deindex), CDNs (404s get cached as successes), monitoring (zero errors while users see broken pages), and `curl`/scripts (`curl -f` won't detect the failure).
35
-
36
- ## timber's Solution
37
-
38
- timber holds the response until it knows the real outcome:
39
-
40
- ```
41
- Request arrives
42
- → Route matched
43
- → proxy.ts runs
44
- → middleware.ts runs
45
- → access.ts runs
46
- → React shell renders (onShellReady)
47
- → ✓ Status code committed ← flush point
48
- → Shell HTML sent to browser
49
- → Suspense boundaries stream in
50
- ```
51
-
52
- The status code commits when **all three** conditions are met:
53
-
54
- 1. Middleware completed without returning a response
55
- 2. All access checks passed (or denied with a real status code)
56
- 3. React's `onShellReady` fired — the synchronous shell rendered without error
57
-
58
- A missing page returns a real `404`. A failed auth check returns a real `403`. A redirect returns a real `302`. The HTTP layer tells the truth.
59
-
60
- ## Before and After the Flush
61
-
62
- **Before the flush** (blocking):
63
-
64
- - `proxy.ts` — global request processing
65
- - `middleware.ts` — route-level request processing
66
- - `access.ts` — authorization gates
67
- - Synchronous component rendering (the shell)
68
-
69
- **After the flush** (streaming):
70
-
71
- - `<Suspense>` boundaries resolve and stream in
72
- - Slow data loads complete
73
- - The page progressively fills in
74
-
75
- ``` leading="none"
76
- ┌─────── flush point
77
- │
78
- ▼
79
- ├────────┤──────────────────────────┤
80
- blocking streaming
81
- (shell) (suspense boundaries)
82
- ```
83
-
84
- Everything before the flush determines the status code. Everything after streams progressively. You control the boundary by choosing what goes inside `<Suspense>` and what doesn't.
85
-
86
- ## Pages Work Without JavaScript
87
-
88
- Because timber holds the flush until the shell is complete, the browser receives a fully-formed HTML document. Content is visible, links work as standard `<a>` tags, and forms submit as standard POSTs. JavaScript adds client-side navigation, interactive components, and streaming updates — but the page works without it.
89
-
90
- This isn't a special mode. It's how timber works by default.
91
-
92
- ## Early Hints
93
-
94
- timber doesn't make you wait for assets while the shell renders. At route-match time — before middleware runs — timber sends `103 Early Hints` with CSS, JS, and font URLs. The browser starts downloading assets while the server is still working:
95
-
96
- ```
97
- 103 Early Hints
98
- Link: </styles/main.css>; rel=preload; as=style
99
- Link: </chunks/page-abc.js>; rel=modulepreload
100
-
101
- ... middleware runs, shell renders ...
102
-
103
- 200 OK
104
- <html>...
105
- ```
106
-
107
- You get the correctness of a held flush with the performance of early resource loading.
108
-
109
- ## How to Think About It
110
-
111
- Primary content — the data that defines whether a page exists, who can see it, and what it contains — should load before the flush. Put it in your components directly.
112
-
113
- Secondary content — recommendations, activity feeds, analytics widgets — can load after the flush. Wrap it in `<Suspense>`.
114
-
115
- The flush point is the dividing line between "what the page _is_" and "what the page _also shows_."
@@ -1,176 +0,0 @@
1
- ---
2
- title: 'Client Navigation'
3
- description: 'The Link component, useRouter, segment cache, and scroll restoration.'
4
- slug: 'client-navigation'
5
- ---
6
-
7
- # Client Navigation
8
-
9
- timber.js supports client-side navigation via the `<Link>` component. Clicking a link fetches an RSC payload from the server and reconciles the DOM without a full page reload.
10
-
11
- ## `<Link>`
12
-
13
- ```tsx
14
- import { Link } from '@timber-js/app/client';
15
-
16
- <Link href="/about">About</Link>
17
- <Link href="/products/123">Product</Link>
18
- <Link href="/dashboard" replace>Dashboard</Link>
19
- ```
20
-
21
- `<Link>` type-checks `href` against the generated route map. Invalid routes produce a TypeScript error.
22
-
23
- ## `useRouter`
24
-
25
- For programmatic navigation:
26
-
27
- ```tsx title="app/components/logout-button.tsx"
28
- 'use client';
29
-
30
- import { useRouter } from '@timber-js/app/client';
31
-
32
- declare function logout(): Promise<void>;
33
-
34
- export function LogoutButton() {
35
- const router = useRouter();
36
-
37
- async function handleLogout() {
38
- await logout();
39
- router.push('/login');
40
- }
41
-
42
- return <button onClick={handleLogout}>Log out</button>;
43
- }
44
- ```
45
-
46
- | Method | Description |
47
- | ---------------------- | ---------------------------------------- |
48
- | `router.push(href)` | Navigate to a new URL |
49
- | `router.replace(href)` | Navigate without adding a history entry |
50
- | `router.refresh()` | Re-fetch the current route's RSC payload |
51
- | `router.back()` | Go back in history |
52
- | `router.forward()` | Go forward in history |
53
-
54
- ## `usePathname`
55
-
56
- Returns the current pathname:
57
-
58
- ```tsx
59
- 'use client';
60
- import { usePathname, Link } from '@timber-js/app/client';
61
-
62
- export function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
63
- const pathname = usePathname();
64
- const isActive = pathname === href;
65
- return (
66
- <Link href={href} className={isActive ? 'font-bold' : ''}>
67
- {children}
68
- </Link>
69
- );
70
- }
71
- ```
72
-
73
- ## `useSelectedLayoutSegment`
74
-
75
- Returns the active segment within a layout — useful for highlighting nav items:
76
-
77
- ```tsx
78
- 'use client';
79
- import { useSelectedLayoutSegment } from '@timber-js/app/client';
80
-
81
- export function DashboardNav() {
82
- const segment = useSelectedLayoutSegment();
83
- // segment is "settings", "projects", etc.
84
- }
85
- ```
86
-
87
- ## `useLinkStatus`
88
-
89
- Track per-link pending state during navigation:
90
-
91
- ```tsx
92
- 'use client';
93
- import { Link, useLinkStatus } from '@timber-js/app/client';
94
-
95
- function NavItemInner({ children }: { children: React.ReactNode }) {
96
- const { isPending } = useLinkStatus();
97
- return <span className={isPending ? 'opacity-50' : ''}>{children}</span>;
98
- }
99
-
100
- export function NavItem({ href, children }: { href: string; children: React.ReactNode }) {
101
- return (
102
- <Link href={href}>
103
- <NavItemInner>{children}</NavItemInner>
104
- </Link>
105
- );
106
- }
107
- ```
108
-
109
- ## Segment Cache
110
-
111
- Opt-in via `clientSegmentCache: true` in `timber.config.ts`. When enabled, the client maintains a mirror of the server's segment tree. On navigation, only changed segments are re-fetched:
112
-
113
- ```ts title="timber.config.ts"
114
- export default {
115
- clientSegmentCache: true,
116
- };
117
- ```
118
-
119
- ```
120
- /dashboard/settings → /dashboard/team
121
-
122
- Root Layout ← sync, mounted → skip
123
- Auth Layout ← sync, mounted → skip
124
- Dashboard Layout ← sync, mounted → skip
125
- Team Page ← new → fetch from server
126
- ```
127
-
128
- Sync layouts stay cached while mounted. Async layouts always re-render. Pages always re-render. Client component state in shared layouts is preserved — counters, form inputs, scroll positions survive navigation.
129
-
130
- When disabled (the default), every client navigation gets a full RSC payload from the server. This is simpler and avoids edge cases with stale cached layouts, at the cost of slightly larger payloads on navigation.
131
-
132
- Back/forward navigation replays cached RSC payloads instantly — no server roundtrip.
133
-
134
- ## Scroll Restoration
135
-
136
- - **Forward navigation** scrolls to the top of the page.
137
- - **Back/forward** restores the saved scroll position.
138
- - **Hash fragments** — `<Link href="/docs/api#install">` commits the full URL (including the `#fragment`) to the address bar and scrolls to the matching element after render, just like a plain `<a>` without JavaScript. If no element matches, navigation falls back to scroll-to-top.
139
-
140
- Pass `scroll={false}` to `<Link>` or `router.push` to preserve the current scroll position:
141
-
142
- ```tsx
143
- <Link href="/dashboard/settings" scroll={false}>Settings</Link>
144
- ```
145
-
146
- ```tsx
147
- router.push('/dashboard/settings', { scroll: false });
148
- ```
149
-
150
- ### Custom Scroll Containers
151
-
152
- If your layout uses a custom scrollable container, add `data-timber-scroll-restoration`:
153
-
154
- ```tsx
155
- <main className="overflow-y-auto h-screen" data-timber-scroll-restoration>
156
- {children}
157
- </main>
158
- ```
159
-
160
- ## `Link.onNavigate`
161
-
162
- Intercept navigation for view transitions:
163
-
164
- ```tsx
165
- <Link
166
- href="/gallery"
167
- onNavigate={(e) => {
168
- if (document.startViewTransition) {
169
- e.preventDefault();
170
- document.startViewTransition(() => e.navigate());
171
- }
172
- }}
173
- >
174
- Gallery
175
- </Link>
176
- ```
@@ -1,166 +0,0 @@
1
- ---
2
- title: 'Configuration'
3
- description: 'timber.config.ts — output mode, adapters, caching, and the two-phase model.'
4
- slug: 'configuration'
5
- ---
6
-
7
- # Configuration
8
-
9
- timber.js is configured through `timber.config.ts` at the project root. Vite configuration lives separately in `vite.config.ts`.
10
-
11
- ## Minimal Config
12
-
13
- ```ts title="timber.config.ts"
14
- export default {
15
- output: 'server',
16
- };
17
- ```
18
-
19
- That's it for most projects. The defaults are sensible.
20
-
21
- ## How the Config Is Loaded
22
-
23
- `timber.config.ts` is loaded by Node.js itself using built-in type stripping — it is not bundled by Vite. This requires Node.js 22.18 or newer, and the file must stick to erasable TypeScript syntax: type annotations, interfaces, and `satisfies` are fine, but enums, namespaces, and constructor parameter properties will fail to load. Your `package.json` should declare `"type": "module"` (the default for new timber projects) so the `export default` syntax parses correctly.
24
-
25
- If the config file fails to load for any reason, every command — `timber dev`, `timber build`, and `timber preview` — stops with an error naming the file. A broken config never silently falls back to defaults.
26
-
27
- ## Output Modes
28
-
29
- | Mode | Server required | Client JS | Server Actions |
30
- | -------------------------------------- | --------------- | ------------------------- | -------------- |
31
- | `'server'` | Yes | Yes | Yes |
32
- | `'static'` | No | Yes (hydration + SPA nav) | Via adapter |
33
- | `'static'` + `clientJavascript: false` | No | None | Build error |
34
-
35
- ```ts title="timber.config.ts"
36
- // Static site with no JavaScript
37
- export default {
38
- output: 'static',
39
- clientJavascript: false,
40
- };
41
- ```
42
-
43
- ## Build Time vs Request Time
44
-
45
- The `output` field shifts work between two phases:
46
-
47
- **Build time** happens once when you run `timber build`. There's no request, no user, no cookies. Route scanning, client boundary discovery, server action extraction, font downloading, and asset bundling all happen here.
48
-
49
- **Request time** happens per incoming HTTP request. Middleware, access checks, server components, caching, cookies, and server actions all run here.
50
-
51
- | `output` | `middleware.ts` | Server components | Server actions |
52
- | ---------- | --------------------- | ------------------------------ | ------------------------------------------- |
53
- | `'server'` | Request time | Request time | Request time |
54
- | `'static'` | **Build time only** | **Build time** (once, to HTML) | **Request time**, split-deployed as endpoints |
55
-
56
- This is why `cookies()` and `headers()` are build errors in static mode — there's no request at the moment components render.
57
-
58
- Dynamic routes (`[param]`) must export `generateStaticSegmentParams` when using `output: 'static'` — the build needs to know which URLs to render. API routes (`route.ts`) are also pre-rendered at build time.
59
-
60
- For each page, the build generates both HTML (for initial loads) and RSC flight data (for client-side SPA navigation). No server is needed at runtime.
61
-
62
- ## Adapters
63
-
64
- Adapters transform the build output for your deployment platform:
65
-
66
- ```ts title="timber.config.ts"
67
- import { cloudflare } from '@timber-js/app/adapters/cloudflare';
68
-
69
- export default {
70
- output: 'server',
71
- adapter: cloudflare(),
72
- };
73
- ```
74
-
75
- ```ts title="timber.config.ts"
76
- import { nitro } from '@timber-js/app/adapters/nitro';
77
-
78
- export default {
79
- output: 'server',
80
- adapter: nitro({ preset: 'node-server' }),
81
- };
82
- ```
83
-
84
- `cloudflare()` for Cloudflare Workers. `nitro({ preset })` for everything else — Node.js, Vercel, Netlify, AWS Lambda, Deno, Bun, Azure.
85
-
86
- ## Cache Handler
87
-
88
- Cache handler configuration lives in a separate `timber.cache.ts` file (not `timber.config.ts`). This keeps runtime cache instances separate from build-time config, preventing build dependencies from leaking into the server bundle.
89
-
90
- ```ts title="timber.cache.ts"
91
- import { MemoryCacheHandler } from '@timber-js/app/cache';
92
-
93
- export default new MemoryCacheHandler();
94
- ```
95
-
96
- The default export is the `CacheHandler` instance. Export `cdnPurge` as a named export for CDN purge handlers. The default handler is in-memory. See [Caching](/docs/caching) for Redis, KV, and custom handlers.
97
-
98
- ## `clientJavascript`
99
-
100
- Control whether client-side JavaScript is included in the build. Useful for static content sites, documentation, and marketing pages that don't need interactivity.
101
-
102
- ```ts title="timber.config.ts"
103
- // Disable all client JS
104
- export default {
105
- output: 'static',
106
- clientJavascript: false,
107
- };
108
- ```
109
-
110
- ```ts title="timber.config.ts"
111
- // No client JS in production, but keep HMR in dev
112
- export default {
113
- output: 'static',
114
- clientJavascript: { disabled: true, enableHMRInDev: true },
115
- };
116
- ```
117
-
118
- | Value | Behavior |
119
- | ------------------------------------------ | ----------------------------------------------------------- |
120
- | `true` (default) | Client JS enabled — hydration, SPA navigation, etc. |
121
- | `false` | All client JS disabled. No hydration, no SPA navigation. |
122
- | `{ disabled: true, enableHMRInDev: true }` | No client JS in production, but HMR still works in dev. |
123
-
124
- Server actions still work — HTML forms submit natively via POST without JavaScript.
125
-
126
- ## All Options
127
-
128
- | Option | Type | Default | Description |
129
- | ------------------- | --------------------------- | ---------------------------- | ---------------------------------------- |
130
- | `output` | `'server' \| 'static'` | `'server'` | Output mode |
131
- | `debug` | `boolean` | `false` | Enable timber debug logging in prod |
132
- | `buildDir` | `string` | `'.timber/dist'` | Build output directory |
133
- | `clientJavascript` | `boolean \| object` | `true` | Control client-side JS |
134
- | `adapter` | `TimberPlatformAdapter` | — | Deployment adapter |
135
- | `serverTiming` | `'detailed' \| 'total' \| false` | `'detailed'` / `'total'` | Server-Timing header |
136
- | `allowedOrigins` | `string[]` | — | CORS / CSRF allowed origins |
137
- | `csrf` | `boolean` | `true` | CSRF protection |
138
- | `limits` | `object` | — | Request body size limits |
139
- | `actions` | `object` | — | Server action behavior |
140
- | `forms` | `object` | — | Form handling (sensitive field stripping) |
141
- | `pageExtensions` | `string[]` | `['tsx', 'ts', 'jsx', 'js']` | File extensions for pages |
142
- | `slowRequestMs` | `number` | `3000` | Slow request warning threshold (ms) |
143
- | `renderTimeoutMs` | `number` | `30000` | Render abort timeout (ms) |
144
- | `devBrowserLogs` | `string` | `'warn'` | Forward browser console to server in dev |
145
- | `dev` | `object` | — | Dev-mode options |
146
- | `budget` | `object` | — | Build-time performance budgets |
147
- | `appDir` | `string` | auto-detected | Override app directory location |
148
- | `mdx` | `object` | — | MDX remark/rehype plugins |
149
- | `actionEncryption` | `object` | — | Server action bound args encryption |
150
- | `reactCompiler` | `boolean \| object` | `true` | React Compiler auto-memoization |
151
- | `sitemap` | `object` | — | Auto-generated sitemap.xml |
152
- | `clientSegmentCache`| `boolean` | `false` | Opt-in client segment cache for partial nav |
153
- | `topLoader` | `object` | enabled | Navigation progress bar |
154
-
155
- For the full type definition, see the [Config API Reference](/docs/api-config).
156
-
157
- ## .gitignore
158
-
159
- Add these to your `.gitignore` — they're generated at build/dev time and should not be committed:
160
-
161
- ```txt title=".gitignore"
162
- .timber
163
- .content-collections
164
- ```
165
-
166
- `.timber` contains the build output (default `.timber/dist`). `.content-collections` contains generated modules from [content collections](/docs/content-collections). `create-timber-app` adds both automatically.