@wular/pnext 0.0.4 → 0.0.7

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 (395) hide show
  1. package/README.md +22 -16
  2. package/bin/pnext +6 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +87 -22
  8. package/reference/config.md +40 -93
  9. package/reference/css.md +42 -33
  10. package/reference/dev.md +26 -33
  11. package/reference/env.md +18 -20
  12. package/reference/getting-started.md +132 -0
  13. package/reference/metadata.md +31 -46
  14. package/reference/navigation.md +65 -105
  15. package/reference/performance.md +21 -142
  16. package/reference/rendering.md +78 -75
  17. package/reference/routing.md +97 -107
  18. package/reference/typegen.md +27 -29
  19. package/src/api/cache.ts +36 -37
  20. package/src/api/client-cache.ts +2 -2
  21. package/src/api/client-navigation.ts +121 -114
  22. package/src/api/dynamic.tsx +58 -55
  23. package/src/api/link.tsx +52 -53
  24. package/src/api/navigation.ts +59 -58
  25. package/src/api/server.ts +152 -144
  26. package/src/api/suspense.ts +4 -4
  27. package/src/cli/adapters/vercel-warm.ts +126 -121
  28. package/src/cli/adapters/vercel.ts +437 -443
  29. package/src/cli/analyze.ts +333 -144
  30. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  31. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  32. package/src/cli/build.ts +976 -978
  33. package/src/cli/create.ts +72 -64
  34. package/src/cli/dev.ts +116 -116
  35. package/src/cli/index.ts +94 -85
  36. package/src/cli/migrate/package-json.ts +73 -71
  37. package/src/cli/migrate/report.ts +32 -33
  38. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  39. package/src/cli/migrate/scan.ts +46 -46
  40. package/src/cli/migrate/spinner.ts +9 -9
  41. package/src/cli/migrate/tsconfig.ts +35 -35
  42. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  43. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  44. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  45. package/src/cli/start.ts +65 -66
  46. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  47. package/src/client/build.ts +802 -768
  48. package/src/client/chunk-fold.ts +245 -240
  49. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  50. package/src/client/entry.ts +162 -147
  51. package/src/client/prebuilt.ts +231 -223
  52. package/src/client/profile.ts +29 -29
  53. package/src/client/react-compiler.ts +20 -15
  54. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  55. package/src/client/reference-stub.ts +51 -51
  56. package/src/client/reference.ts +19 -19
  57. package/src/{api → client}/router/events.ts +32 -17
  58. package/src/{api → client}/router/history.ts +16 -16
  59. package/src/{api → client}/router/hub.ts +52 -53
  60. package/src/{api/router.ts → client/router/index.ts} +61 -60
  61. package/src/{api → client}/router/policies.ts +24 -24
  62. package/src/{api → client}/router/runtime.ts +2022 -1995
  63. package/src/{api → client}/router/types.ts +98 -98
  64. package/src/compat/actions/client-plugin.ts +42 -43
  65. package/src/compat/actions/client-stub.ts +14 -14
  66. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  67. package/src/compat/actions/config.ts +63 -64
  68. package/src/compat/actions/detect.ts +68 -68
  69. package/src/compat/actions/discovery.ts +105 -105
  70. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  71. package/src/compat/actions/early-submit.ts +1 -1
  72. package/src/compat/actions/endpoint.ts +198 -198
  73. package/src/compat/actions/extensions.ts +811 -0
  74. package/src/compat/actions/flight.ts +23 -23
  75. package/src/compat/actions/form-state.ts +34 -34
  76. package/src/compat/actions/hoist.ts +382 -236
  77. package/src/compat/actions/ids.ts +7 -12
  78. package/src/compat/actions/index.ts +8 -8
  79. package/src/compat/actions/instances.ts +46 -46
  80. package/src/compat/actions/origin.ts +47 -48
  81. package/src/compat/actions/protocol.ts +22 -22
  82. package/src/compat/actions/registry.ts +19 -19
  83. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  84. package/src/compat/actions/rewrite.ts +125 -125
  85. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  86. package/src/compat/actions/serve.ts +132 -136
  87. package/src/compat/actions/server-tag.ts +4 -4
  88. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  89. package/src/compat/actions/unrecognized-error.ts +4 -4
  90. package/src/compat/{index.ts → aliases.ts} +112 -109
  91. package/src/compat/bundler/bun-externals.ts +18 -18
  92. package/src/compat/bundler/cjs-exports.ts +271 -223
  93. package/src/compat/bundler/config.ts +116 -111
  94. package/src/compat/bundler/externals.ts +12 -12
  95. package/src/compat/bundler/import-meta-url.ts +19 -19
  96. package/src/compat/bundler/modularize-imports.ts +36 -33
  97. package/src/compat/bundler/new-url-asset.ts +22 -24
  98. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  99. package/src/compat/bundler/polyfill.ts +28 -28
  100. package/src/compat/bundler/react-compiler.ts +35 -29
  101. package/src/compat/bundler/react-profiler.tsx +11 -11
  102. package/src/compat/bundler/relay-transform.ts +48 -47
  103. package/src/compat/bundler/require-context.ts +119 -113
  104. package/src/compat/bundler/resolve-extensions.ts +19 -19
  105. package/src/compat/bundler/source-cache.ts +25 -25
  106. package/src/compat/bundler/static-imports.ts +7 -7
  107. package/src/compat/bundler/symlink-imports.ts +46 -46
  108. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  109. package/src/compat/bundler/wasm.ts +58 -60
  110. package/src/compat/bundler/webpack-loaders.ts +254 -241
  111. package/src/compat/bundler/worker.ts +101 -104
  112. package/src/compat/cache/build-flags.ts +29 -29
  113. package/src/compat/cache/build-prerender-errors.ts +40 -44
  114. package/src/compat/cache/custom-handler.ts +60 -53
  115. package/src/compat/cache/fetch-patch.ts +240 -240
  116. package/src/compat/cache/handler.ts +27 -31
  117. package/src/compat/cache/modern-handler.ts +148 -126
  118. package/src/compat/cache/resume-data-cache.ts +47 -45
  119. package/src/compat/cache/revalidate.ts +233 -232
  120. package/src/compat/cache/runtime-error.ts +35 -35
  121. package/src/compat/cache/use-cache-transform.ts +442 -417
  122. package/src/compat/cache/use-cache.ts +650 -614
  123. package/src/compat/cache-control.ts +140 -142
  124. package/src/compat/client/base-path.ts +21 -20
  125. package/src/compat/client/css-order.ts +18 -18
  126. package/src/compat/client/errors/bare-boundary.ts +11 -11
  127. package/src/compat/client/errors/control-flow.ts +23 -23
  128. package/src/compat/client/errors/error-boundary.ts +61 -61
  129. package/src/compat/client/errors/global-error.ts +101 -91
  130. package/src/compat/client/errors/install.ts +71 -72
  131. package/src/compat/client/errors/lazy.ts +23 -23
  132. package/src/compat/client/errors/primitive-throw.ts +47 -45
  133. package/src/compat/client/errors/soft-refresh.ts +4 -4
  134. package/src/compat/client/link-status.ts +33 -33
  135. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  136. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  137. package/src/compat/client/navigation-scroll.ts +63 -63
  138. package/src/compat/client/optimistic-routing.ts +93 -88
  139. package/src/compat/client/prefetch-cache.ts +23 -24
  140. package/src/compat/client/route-announcer.ts +35 -35
  141. package/src/compat/client/segment-cache-policy.ts +20 -20
  142. package/src/compat/client/segment-cache.ts +309 -315
  143. package/src/compat/client/segment-prefetch.ts +127 -132
  144. package/src/compat/client/trailing-slash.ts +5 -4
  145. package/src/compat/css/chunking.ts +113 -116
  146. package/src/compat/css/inline-css.ts +21 -21
  147. package/src/compat/css/lightningcss.ts +37 -38
  148. package/src/compat/css/modules.ts +161 -175
  149. package/src/compat/css/nonce.ts +7 -7
  150. package/src/compat/css/sass-plugin.ts +18 -21
  151. package/src/compat/css/sass.ts +150 -152
  152. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  153. package/src/compat/css/styled-jsx.ts +21 -21
  154. package/src/compat/edge-runtime.ts +27 -27
  155. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  156. package/src/compat/export/client.ts +29 -31
  157. package/src/compat/export/{index.ts → emit.ts} +111 -110
  158. package/src/compat/export/standalone.ts +62 -54
  159. package/src/compat/image-optimizer/cache.ts +51 -49
  160. package/src/compat/image-optimizer/detect.ts +52 -52
  161. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  162. package/src/compat/image-optimizer/source.ts +77 -80
  163. package/src/compat/lifecycle/after-scope.ts +26 -26
  164. package/src/compat/lifecycle/after.ts +48 -45
  165. package/src/compat/lifecycle/error-funnel.ts +98 -102
  166. package/src/compat/lifecycle/error-serialize.ts +30 -26
  167. package/src/compat/lifecycle/error-ui.ts +67 -32
  168. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  169. package/src/compat/lifecycle/instrumentation.ts +85 -85
  170. package/src/compat/lifecycle/node-console.ts +11 -11
  171. package/src/compat/lifecycle/testmode.ts +160 -132
  172. package/src/compat/mdx/compile.ts +60 -57
  173. package/src/compat/mdx/plugin.ts +11 -11
  174. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  175. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  176. package/src/compat/metadata.ts +75 -81
  177. package/src/compat/next/cache.ts +68 -69
  178. package/src/compat/next/canonical-url.ts +9 -9
  179. package/src/compat/next/client-cache.ts +19 -19
  180. package/src/compat/next/client-navigation.ts +112 -116
  181. package/src/compat/next/client-only.ts +1 -1
  182. package/src/compat/next/client-script.tsx +99 -93
  183. package/src/compat/next/client-server.ts +10 -10
  184. package/src/compat/next/config-loader.ts +176 -173
  185. package/src/compat/next/config.ts +7 -7
  186. package/src/compat/next/constants.cjs +6 -6
  187. package/src/compat/next/constants.ts +6 -6
  188. package/src/compat/next/custom-server.ts +26 -24
  189. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  190. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  191. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  192. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  193. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  194. package/src/compat/next/dynamic.tsx +21 -18
  195. package/src/compat/next/error.tsx +52 -52
  196. package/src/compat/next/font/cache.ts +74 -74
  197. package/src/compat/next/font/google.ts +2 -2
  198. package/src/compat/next/font/index.ts +1 -1
  199. package/src/compat/next/font/local.ts +3 -3
  200. package/src/compat/next/font/runtime-client.ts +17 -15
  201. package/src/compat/next/font/runtime.ts +443 -408
  202. package/src/compat/next/font/shared.ts +120 -108
  203. package/src/compat/next/form.tsx +63 -63
  204. package/src/compat/next/head.tsx +2 -2
  205. package/src/compat/next/headers.ts +103 -95
  206. package/src/compat/next/image/client.tsx +220 -0
  207. package/src/compat/next/image/config.ts +56 -58
  208. package/src/compat/next/image/optimizer.ts +40 -36
  209. package/src/compat/next/image/patterns.ts +37 -40
  210. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  211. package/src/compat/next/image/shared.ts +65 -57
  212. package/src/compat/next/image/static-metadata.ts +98 -107
  213. package/src/compat/next/image/validate.ts +79 -88
  214. package/src/compat/next/image.tsx +19 -23
  215. package/src/compat/next/index.ts +1 -1
  216. package/src/compat/next/legacy-image.tsx +59 -60
  217. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  218. package/src/compat/next/link.tsx +158 -158
  219. package/src/compat/next/navigation.cjs +12 -3
  220. package/src/compat/next/navigation.ts +48 -50
  221. package/src/compat/next/offline.ts +27 -27
  222. package/src/compat/next/og.ts +121 -124
  223. package/src/compat/next/preferred-region.ts +13 -14
  224. package/src/compat/next/redirects.ts +58 -56
  225. package/src/compat/next/resource-hints.ts +73 -76
  226. package/src/compat/next/rewrites.ts +130 -133
  227. package/src/compat/next/root-params.ts +45 -45
  228. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  229. package/src/compat/next/router.cjs +4 -2
  230. package/src/compat/next/router.ts +58 -61
  231. package/src/compat/next/script.tsx +108 -108
  232. package/src/compat/next/server-only.ts +1 -1
  233. package/src/compat/next/server.ts +19 -19
  234. package/src/compat/next/svgr.ts +18 -17
  235. package/src/compat/next/telemetry.ts +24 -24
  236. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  237. package/src/compat/next/user-agent.ts +53 -49
  238. package/src/compat/next/web-vitals.ts +22 -24
  239. package/src/compat/otel/api.ts +41 -41
  240. package/src/compat/otel/client-trace-metadata.ts +25 -27
  241. package/src/compat/otel/fetch-span.ts +29 -29
  242. package/src/compat/otel/tracer.ts +331 -331
  243. package/src/compat/pages/api.ts +456 -0
  244. package/src/compat/pages/client-plugin.ts +36 -36
  245. package/src/compat/pages/router-state.ts +34 -34
  246. package/src/compat/pages/{index.ts → router.ts} +130 -135
  247. package/src/compat/ppr/io.ts +12 -12
  248. package/src/compat/ppr/missing-root-params.ts +34 -38
  249. package/src/compat/ppr/root-params-scan.ts +66 -66
  250. package/src/compat/ppr/root-params-transform.ts +24 -26
  251. package/src/compat/ppr/root-params.ts +30 -30
  252. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  253. package/src/compat/protocol.ts +71 -70
  254. package/src/compat/react/action-state.ts +47 -48
  255. package/src/compat/react/client-lite.ts +15 -15
  256. package/src/compat/react/client.ts +4 -4
  257. package/src/compat/react/compiler-runtime.ts +11 -11
  258. package/src/compat/react/dom-client.ts +44 -44
  259. package/src/compat/react/dom-react-server.ts +10 -16
  260. package/src/compat/react/dom-server.ts +10 -10
  261. package/src/compat/react/dom.ts +52 -52
  262. package/src/compat/react/hooks-extra.ts +34 -35
  263. package/src/compat/react/parity.ts +64 -61
  264. package/src/compat/react/preact.ts +81 -82
  265. package/src/compat/react/react-server.ts +28 -28
  266. package/src/compat/react/router-shim.ts +1 -1
  267. package/src/compat/react/server-component-use.ts +8 -8
  268. package/src/compat/react/server-inserted-html.ts +30 -31
  269. package/src/compat/react/server.ts +53 -55
  270. package/src/compat/react/use.ts +32 -32
  271. package/src/compat/react/view-transition.ts +20 -20
  272. package/src/compat/register/actions.ts +35 -824
  273. package/src/compat/register/boot.ts +41 -41
  274. package/src/compat/register/build-tier.ts +5 -5
  275. package/src/compat/register/build.ts +74 -70
  276. package/src/compat/register/bundler.ts +161 -156
  277. package/src/compat/register/cache.ts +20 -20
  278. package/src/compat/register/client-errors.ts +3 -3
  279. package/src/compat/register/config.ts +6 -6
  280. package/src/compat/register/css-extras.ts +30 -34
  281. package/src/compat/register/edge-runtime.ts +3 -3
  282. package/src/compat/register/errors.ts +10 -12
  283. package/src/compat/register/export.ts +16 -16
  284. package/src/compat/register/font.ts +11 -11
  285. package/src/compat/register/hooks.ts +2 -2
  286. package/src/compat/register/image.ts +40 -40
  287. package/src/compat/register/index.ts +59 -62
  288. package/src/compat/register/instrumentation-client.ts +10 -10
  289. package/src/compat/register/lifecycle.ts +25 -28
  290. package/src/compat/register/mdx.ts +10 -10
  291. package/src/compat/register/middleware.ts +226 -16
  292. package/src/compat/register/otel.ts +80 -88
  293. package/src/compat/register/pages-api.ts +10 -463
  294. package/src/compat/register/ppr.ts +16 -16
  295. package/src/compat/register/protocol.ts +17 -18
  296. package/src/compat/register/proxy.ts +49 -51
  297. package/src/compat/register/render.ts +79 -76
  298. package/src/compat/register/routing.ts +162 -159
  299. package/src/compat/register/segment.ts +24 -1897
  300. package/src/compat/register/static-image.ts +3 -3
  301. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  302. package/src/compat/register/typed-routes.ts +14 -14
  303. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  304. package/src/compat/register/validation.ts +18 -18
  305. package/src/compat/segment/loading-boundary.ts +43 -45
  306. package/src/compat/segment/page-slot.ts +69 -69
  307. package/src/compat/segment/serve.ts +1884 -0
  308. package/src/compat/segment/tree.ts +113 -112
  309. package/src/compat/segment/vary-key.ts +38 -38
  310. package/src/compat/segment/vary-params.ts +138 -142
  311. package/src/compat/static-params.ts +14 -12
  312. package/src/compat/tsconfig-defaults.ts +87 -91
  313. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  314. package/src/compat/typecheck/worker.ts +15 -12
  315. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  316. package/src/compat/typed-routes/manifest.ts +174 -170
  317. package/src/compat/typed-routes/typegen.ts +127 -110
  318. package/src/compat/validation/errors.ts +16 -19
  319. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  320. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  321. package/src/compat-bootstrap.ts +16 -16
  322. package/src/config.ts +86 -69
  323. package/src/css/build.ts +230 -227
  324. package/src/css/postcss.ts +79 -80
  325. package/src/css/worker.ts +14 -15
  326. package/src/dev/client-actions.ts +10 -10
  327. package/src/dev/client-chunk-store.ts +27 -27
  328. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  329. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  330. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  331. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  332. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  333. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  334. package/src/dev/server.ts +804 -820
  335. package/src/env.ts +46 -43
  336. package/src/extensions.ts +491 -478
  337. package/src/index.ts +8 -8
  338. package/src/internal.ts +20 -23
  339. package/src/{islands → render}/boundary-error.ts +3 -3
  340. package/src/render/hooks.ts +71 -71
  341. package/src/render/island-context.ts +14 -14
  342. package/src/render/metadata.ts +310 -310
  343. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  344. package/src/{ppr.ts → render/ppr.ts} +244 -245
  345. package/src/render/renderer.ts +2087 -2082
  346. package/src/render/resource-hints.ts +16 -17
  347. package/src/render/slots.tsx +224 -235
  348. package/src/{islands → render}/static-children.ts +9 -12
  349. package/src/{islands → render}/static-slots.ts +37 -37
  350. package/src/{cache/context.ts → request/cache.ts} +20 -20
  351. package/src/request/context.ts +107 -107
  352. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  353. package/src/resolve/engine.ts +90 -77
  354. package/src/resolve/imports.ts +475 -463
  355. package/src/resolve/scan-facts.ts +318 -296
  356. package/src/resolve/source-text.ts +37 -37
  357. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  358. package/src/routing/forwarded.ts +19 -19
  359. package/src/routing/handler.ts +84 -91
  360. package/src/routing/href.ts +87 -70
  361. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  362. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  363. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  364. package/src/routing/routes.ts +827 -815
  365. package/src/routing/slots.ts +164 -160
  366. package/src/runtime/loader.ts +954 -0
  367. package/src/{dev → runtime}/module-cache.ts +309 -287
  368. package/src/{dev → runtime}/module-generations.ts +9 -9
  369. package/src/{dev → runtime}/module-transform.ts +81 -72
  370. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  371. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  372. package/src/runtime/vendor.ts +428 -406
  373. package/src/styles.d.ts +9 -0
  374. package/src/types.ts +324 -335
  375. package/src/utils/ansi.ts +5 -5
  376. package/src/utils/{source.ts → code.ts} +15 -12
  377. package/src/utils/content-type.ts +3 -3
  378. package/src/utils/decode.ts +2 -2
  379. package/src/utils/dev-profile.ts +13 -13
  380. package/src/utils/error-log.ts +6 -6
  381. package/src/utils/esbuild.ts +18 -18
  382. package/src/utils/fs-cache.ts +13 -13
  383. package/src/utils/fs.ts +57 -49
  384. package/src/utils/html.ts +20 -24
  385. package/src/utils/native-require.ts +8 -8
  386. package/src/utils/serialize.ts +139 -146
  387. package/src/utils/verbose.ts +18 -18
  388. package/reference/overview.md +0 -35
  389. package/src/cli/analyze-print.ts +0 -181
  390. package/src/compat/middleware/manifest.ts +0 -210
  391. package/src/compat/next/image-client.tsx +0 -215
  392. package/src/compat/next/link-usage.ts +0 -29
  393. package/src/css/index.ts +0 -2
  394. package/src/render/index.ts +0 -1
  395. package/src/style-modules.d.ts +0 -9
@@ -1,149 +1,109 @@
1
1
  # Navigation
2
2
 
3
- pnext navigation APIs are split by runtime so server routes do not import browser router code.
4
-
5
- ## Typed Hrefs
6
-
7
- Use `href()` when building URLs outside JSX.
8
-
9
- ```tsx
10
- import { href } from '@wular/pnext/navigation';
11
-
12
- const userHref = href('/users/[id]', {
13
- params: { id: 'ada' },
14
- search: { tab: 'runs' },
15
- });
16
- ```
17
-
18
- Dynamic params are checked from generated route types. Routes without params do not take a `params` object.
3
+ Links, prefetching, redirects, and the client router. Server and browser APIs sit in separate entry points, so a server route never pulls in router code meant for the browser.
19
4
 
20
5
  ## Link
21
6
 
22
- `<Link>` renders a normal anchor with typed route params and pnext prefetch metadata.
7
+ Renders a normal anchor. The href is a route template, and params are checked against the generated route types, so a wrong name or a missing param fails TypeScript. Use a plain anchor for external URLs and for links you do not want typed.
23
8
 
24
9
  ```tsx
25
- import { Link } from '@wular/pnext/link';
10
+ import { Link } from '@wular/pnext/link'
26
11
 
27
12
  export function Nav() {
28
13
  return (
29
- <Link href="/users/[id]" params={{ id: 'ada' }} search={{ tab: 'runs' }}>
14
+ <Link
15
+ href="/users/[id]"
16
+ params={{ id: 'ada' }}
17
+ search={{ tab: 'runs' }}
18
+ >
30
19
  Ada
31
20
  </Link>
32
- );
21
+ )
33
22
  }
34
23
  ```
35
24
 
36
- Supported props:
37
-
38
- - `href`: generated route path.
39
- - `params`: required for dynamic routes.
40
- - `search`: query params.
41
- - `hash`: hash fragment.
42
- - `prefetch`: `false`, `'intent'`, `'visible'`, or `'load'`.
43
- - `replace`: marks the navigation as history replace.
44
- - `scroll={false}`: preserves scroll on client navigation.
45
- - `onNavigate`: client-only same-origin navigation callback that can call `preventDefault()`.
46
- - `onPrefetchStart` / `onPrefetchFinish`: fire when this link's prefetch begins and settles, in every prefetch mode.
47
-
48
- Use a normal `<a>` for external URLs or intentionally untyped links.
25
+ | Prop | Value |
26
+ | ------------------------------------- | ------------------------------------------------------------- |
27
+ | `href` | Generated route path. |
28
+ | `params` | Required for dynamic routes. |
29
+ | `search` | Query params. |
30
+ | `hash` | Hash fragment. |
31
+ | `prefetch` | `false`, `'intent'`, `'visible'`, or `'load'`. |
32
+ | `replace` | Marks the navigation as a history replace. |
33
+ | `scroll` | `false` preserves scroll on client navigation. |
34
+ | `onNavigate` | Same-origin client callback that can call `preventDefault()`. |
35
+ | `onPrefetchStart`, `onPrefetchFinish` | Fire when this link's prefetch begins and settles. |
49
36
 
50
- ## Soft Navigation
37
+ ## Prefetching
51
38
 
52
- `<Link>` clicks and `router.push()`/`router.replace()` swap the page in place instead of reloading the document, so shared chunks and CSS are never re-downloaded. Back and forward stay soft and restore scroll. Cross-origin targets, non-HTML responses, and fetch failures fall back to a full page load.
39
+ Prefetch warms the target page and its assets. Set the mode per link with the `prefetch` prop, as in `<Link prefetch="intent">`.
53
40
 
54
- `prefetch` on `<Link>` warms the target page and its assets:
41
+ - `'visible'` is the default, and fires when the link enters the viewport.
42
+ - `'intent'` fires on hover, touch, or focus.
43
+ - `'load'` fires as soon as the link renders.
44
+ - `false` never prefetches. Pair it with `router.prefetch(href)` for manual control.
55
45
 
56
- - `'intent'` (default): on hover, touch, or focus.
57
- - `'visible'`: when the link enters the viewport.
58
- - `'load'`: as soon as the link renders.
59
- - `false`: never; pair with `router.prefetch(href)` for manual control.
46
+ The app-wide default can be set with the `prefetch` field in `pnext.config.ts`, described in [Config](./config.md).
60
47
 
61
- Prefetched pages expire after 30 seconds and each navigation refetches on the next visit, so pages always reflect fresh server state. Prefetch is a no-op under `pnext dev`.
48
+ Requests use low network priority. At most four run at once, though the hover-intent lane allows up to twelve. The core fallback expiry is five minutes. Prefetch does nothing in development.
62
49
 
63
- ## Redirects
50
+ ## Soft navigation
64
51
 
65
- Use `notFound()` to render the nearest `not-found.tsx` fallback with a 404 response.
52
+ Link clicks and router pushes swap the page in place instead of reloading the document, so shared chunks and CSS are never re-downloaded. Back and forward stay soft and restore scroll. Cross-origin targets, non-HTML responses, and fetch failures fall back to a full page load.
66
53
 
67
- Use `redirect()` to stop server rendering and return a redirect response. In the browser it navigates directly.
54
+ ## Redirects and not found
68
55
 
69
56
  ```tsx
70
- import { redirect } from '@wular/pnext/navigation';
57
+ import { href, notFound, redirect } from '@wular/pnext/navigation'
71
58
 
72
- export default async function Page() {
73
- const user = await getUser();
74
- if (!user) redirect('/login');
75
- return <Dashboard user={user} />;
76
- }
59
+ redirect('/login')
60
+ redirect('/login', 308)
61
+ redirect(href('/users/[id]', { params: { id: 'ada' } }))
62
+ notFound()
77
63
  ```
78
64
 
79
- `redirect()` returns a temporary 307 redirect on the server. `permanentRedirect()` returns 308.
80
-
81
- Client event handlers can call `redirect()` too. Keep it as the last statement, or `return redirect(...)`, because browser navigation does not synchronously stop the rest of the handler. Use `useRouter().push()` or `useRouter().replace()` when you want explicit history control.
65
+ `redirect()` stops server rendering and returns a redirect response. Call it from pages, server components, route handlers, and any server helper used during rendering. Validate targets that come from user input.
82
66
 
83
- For typed dynamic redirects, build the URL with `href()`:
67
+ The status is 307 unless you pass 303, 307, or 308. `permanentRedirect()` returns 308.
84
68
 
85
- ```tsx
86
- import { href, redirect } from '@wular/pnext/navigation';
69
+ `notFound()` renders the nearest `not-found` fallback with a 404 response.
87
70
 
88
- redirect(href('/users/[id]', { params: { id: 'ada' } }));
89
- ```
71
+ In the browser, `redirect()` navigates directly. Keep it as the last statement of an event handler, or return it, because browser navigation does not synchronously stop the rest of the handler. Use the router when you want explicit history control.
90
72
 
91
- ## Client Hooks
73
+ ## href()
92
74
 
93
- Client navigation hooks live in `@wular/pnext/navigation/client`.
75
+ Builds a typed URL outside JSX. Routes without params take no params object.
94
76
 
95
- ```tsx
96
- 'use client';
97
-
98
- import { useRouter, useSearchParams } from '@wular/pnext/navigation/client';
99
-
100
- export function SortButton() {
101
- const router = useRouter();
102
- const searchParams = useSearchParams();
103
-
104
- return (
105
- <button
106
- onClick={() => {
107
- const search = new URLSearchParams(searchParams.toString());
108
- search.set('sort', 'asc');
109
- router.replace('/products', { search, scroll: false });
110
- }}
111
- >
112
- Sort
113
- </button>
114
- );
115
- }
77
+ ```ts
78
+ href('/users/[id]', {
79
+ params: { id: 'ada' },
80
+ search: { tab: 'runs' },
81
+ })
116
82
  ```
117
83
 
118
- Available hooks:
119
-
120
- - `useRouter()`: `push`, `replace`, `prefetch`, `refresh`, `back`, and `forward`.
121
- - `usePathname()`: current browser pathname.
122
- - `useSearchParams()`: read-only URL search params.
123
- - `useParams<Route>()`: current route params from the initial route state.
124
- - `useRoute<Route>()`: pathname, route template, params, and search params together.
125
- - `useLinkStatus()`: pending shape for link feedback.
84
+ ## Client hooks
126
85
 
127
- `push()` and `replace()` are typed like `href()`. They soft-navigate when the path or query changes and fall back to the History API for same-URL hash updates. Cross-origin targets become a full page load. `refresh()` re-fetches the current route in place. `useLinkStatus()` currently always reports `{ pending: false }`.
86
+ Exported from `@wular/pnext/navigation/client`, for use in components marked `"use client"`.
128
87
 
129
- ## Dynamic Components
88
+ | Hook | Returns |
89
+ | -------------------- | ------------------------------------------------------------- |
90
+ | `useRouter()` | `push`, `replace`, `prefetch`, `refresh`, `back`, `forward`. |
91
+ | `usePathname()` | Current browser pathname. |
92
+ | `useSearchParams()` | Read-only URL search params. |
93
+ | `useParams<Route>()` | Current route params from the initial route state. |
94
+ | `useRoute<Route>()` | Pathname, route template, params, and search params together. |
95
+ | `useLinkStatus()` | Currently always `{ pending: false }`. |
130
96
 
131
- Use `dynamic()` for lazily loaded Client Components.
97
+ Router pushes and replaces are typed like links. They soft-navigate when the path or query changes, and fall back to the History API for same-URL hash updates. Cross-origin targets become a full page load. Refreshing re-fetches the current route in place.
132
98
 
133
- ```tsx
134
- import { dynamic } from '@wular/pnext/dynamic';
99
+ `useSearchParams()` returns a `ReadonlyURLSearchParams`, whose mutation methods throw. Copy it before changing it.
135
100
 
136
- const Chart = dynamic(() => import('./chart').then(module => module.Chart), {
137
- load: 'visible',
138
- });
139
- ```
140
-
141
- For default exports, use the shorter literal form:
142
-
143
- ```tsx
144
- const Chart = dynamic('./chart');
101
+ ```ts
102
+ const search = new URLSearchParams(searchParams.toString())
103
+ search.set('sort', 'asc')
104
+ router.replace('/products', { search, scroll: false })
145
105
  ```
146
106
 
147
- Literal dynamic imports work from Server Components and Client Components. If the target module starts with `"use client"`, pnext renders it as a Client Component island; otherwise it renders on the server.
107
+ ## Auth interrupts
148
108
 
149
- Dynamic Client Components loaded from Server Components render HTML on the server by default and hydrate when the chunk loads. Pass `ssr: false` for browser-only output. `load: 'visible'` implies browser-only output unless `ssr: true` is set. Inside Client Components, `dynamic()` is a browser lazy loader.
109
+ `forbidden()` and `unauthorized()` throw `PNextForbiddenError` and `PNextUnauthorizedError`, both exported from the server navigation module along with `ReadonlyURLSearchParams`. The functions are core exports, but rendering them as 403 and 401 responses needs the experimental `compat.next` auth interrupts. See [Routing](./routing.md#convention-files).
@@ -1,155 +1,34 @@
1
1
  # Performance
2
2
 
3
- Measured against Next.js: the same fixture source runs under both frameworks and `bun bench` measures them side by side, on a Blacksmith 4-vCPU CI runner (full machine details below). The ranges below span the fixtures hello-world, an SSR site, and a mid-size admin dashboard (30 routes, 18 client islands):
3
+ Measured results against Next.js, and what a route ships to the browser. The numbers come from running the same hello-world, SSR, and 30-route dashboard fixtures under both frameworks. Ratios are Next.js time or memory divided by pnext, so a larger number favors pnext.
4
4
 
5
- | | pnext vs Next.js |
6
- | --------------------------- | ---------------------------------: |
7
- | Dev server ready | **3.8–4.3× faster** |
8
- | First page, cold | **10–12× faster** |
9
- | Warm request (dev) | **1.64.7× faster** |
10
- | HMR save visible | **0.32×** ¹ |
11
- | Production build | **7.2–8.8× faster** |
12
- | Warm request (prod) | **2–3.4× faster** |
13
- | Prod server ready | **1.8–2.3× faster** |
14
- | Dev server memory | **3.4–4.1× less** |
15
- | Build peak memory | **3–3.7× less** |
16
- | Framework install size | **59× smaller** (7.1 MB vs 421 MB) |
17
- | First-page client JS (gzip) | **9.5–65× less** |
5
+ | Metric | Result across the three fixtures |
6
+ | ----------------------------- | ------------------------------------------------------------------------------------- |
7
+ | Dev first page HTML | **10.112.4× faster** |
8
+ | HMR save to visible content | **2.0× faster** on hello-world; **3.1–3.9× slower** on the SSR and dashboard fixtures |
9
+ | Production build time | **7.28.8× faster** |
10
+ | Dev server memory | **3.44.1× less** |
11
+ | Production cold start (ready) | **1.8–2.3× faster** |
18
12
 
19
- ¹ pnext live-reloads (no client HMR runtime; state doesn't survive a save) — the metric
20
- is save → fresh HTML, which both models support. Scoped invalidation fixed the small
21
- fixture (21.9 ms, ahead of Next); the larger fixtures still trail on this runner
22
- (264/300 ms vs 68/98 ms) and the remaining cost is under investigation.
13
+ For interactive tables and per-fixture values, see [pnext.dev/benchmarks](https://www.pnext.dev/benchmarks).
23
14
 
24
- Nothing is prebundled the dev server compiles what a request needs and caches it content-addressed, which is why readiness doesn't scale with app size. The production server idles at parity with Next (~85–87 MB) while answering warm requests 2–3.4× faster.
15
+ ## What ships to the browser
25
16
 
26
- The tables below are one full run's absolute numbers, pasted from `bun bench` output (the CI run's `bench-output` artifact carries the same data as `bench.json`).
17
+ Server-only routes ship 0 KB of route JavaScript, because they need no browser runtime to render their content.
27
18
 
28
- - Date: 2026-08-20 (actions run 32364720143)
29
- - Machine: CI — Blacksmith 4 vCPU (Intel Xeon), 16 GB RAM, Ubuntu 22.04 x64
30
- - Bun: 1.3.10
31
- - Next.js: 16.2.12
32
- - Runs per metric: 5 (first discarded), medians reported
19
+ For client navigation and prefetching, the prefetch-only runtime is 348 B gzip. The combined router and hydrator runtime is 4.47 KB gzip.
33
20
 
34
- Every fixture under `bench/fixtures/` runs unmodified on both frameworks, so the
35
- two columns render the same app from the same source.
21
+ Routes using the Next compatibility layer carry its navigation client, so their client-JS numbers are not the core zero-JS case.
36
22
 
37
- ## Fixture: hello-world
23
+ ## The HMR result
38
24
 
39
- | Metric | pnext | Next.js | Ratio |
40
- | --- | --- | --- | --- |
41
- | Dev cold start (ready) | 56.4 ms | 212.3 ms | 3.76x |
42
- | Dev first page HTML | 79.9 ms | 993.5 ms | 12.44x |
43
- | Dev warm request (p50 of 7) | 3.0 ms | 14.0 ms | 4.69x |
44
- | Dev server RSS (ready + 7 warm) | 130.8 MB | 536.4 MB | 4.10x |
45
- | HMR save → visible | 21.9 ms | 44.8 ms | 2.04x |
46
- | Prod build (wall) | 291.3 ms | 2569.4 ms | 8.82x |
47
- | Prod build peak RSS | 124.2 MB | 455.8 MB | 3.67x |
48
- | Prod start (ready) | 60.7 ms | 110.9 ms | 1.83x |
49
- | Prod warm request (p50 of 7) | 0.6 ms | 1.2 ms | 2.05x |
50
- | Prod server RSS (ready + 7 warm) | 85.2 MB | 85.7 MB | 1.01x |
51
- | Framework install size | 7.1 MB | 420.7 MB | 59.14x |
52
- | First-page client JS (raw) | 4.43 KB | 502.47 KB | 113.36x |
53
- | First-page client JS (gzip) | 2.19 KB | 141.78 KB | 64.67x |
54
- | First-page JS files | 3 | 5 | 1.67x |
55
- | Zero-island route client JS | 4.43 KB | 502.47 KB | 113.36x |
25
+ HMR is slower than Next.js on the larger fixtures and is under investigation. pnext uses live reload rather than a client HMR runtime, so component state does not survive a save. The measurement above is the time until the page serves fresh visible content.
56
26
 
57
- Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
27
+ ## How these were measured
58
28
 
59
- ## Fixture: ssr
29
+ These results are from **2026-08-20**, using Bun **1.3.10** and Next.js **16.2.12** on a Blacksmith 4-vCPU (Intel Xeon), 16 GB RAM, Ubuntu 22.04 x64 CI runner. Each metric used five runs, discarded the first, and reports medians.
60
30
 
61
- | Metric | pnext | Next.js | Ratio |
62
- | --- | --- | --- | --- |
63
- | Dev cold start (ready) | 56.0 ms | 212.8 ms | 3.80x |
64
- | Dev first page HTML | 93.5 ms | 1043.3 ms | 11.16x |
65
- | Dev warm request (p50 of 7) | 16.5 ms | 26.4 ms | 1.60x |
66
- | Dev server RSS (ready + 7 warm) | 151.3 MB | 553.6 MB | 3.66x |
67
- | HMR save → visible | 264.3 ms | 68.3 ms | 0.26x |
68
- | Prod build (wall) | 310.5 ms | 2738.8 ms | 8.82x |
69
- | Prod build peak RSS | 136.8 MB | 456.6 MB | 3.34x |
70
- | Prod start (ready) | 57.1 ms | 105.5 ms | 1.85x |
71
- | Prod warm request (p50 of 7) | 0.5 ms | 1.3 ms | 2.50x |
72
- | Prod server RSS (ready + 7 warm) | 86.0 MB | 85.9 MB | 1.00x |
73
- | Framework install size | 7.1 MB | 420.7 MB | 59.14x |
74
- | First-page client JS (raw) | 27.36 KB | 502.78 KB | 18.37x |
75
- | First-page client JS (gzip) | 11.39 KB | 142.03 KB | 12.47x |
76
- | First-page JS files | 4 | 6 | 1.50x |
77
- | Zero-island route client JS | 4.46 KB | 502.47 KB | 112.56x |
78
-
79
- Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
80
-
81
- ## Fixture: dashboard
82
-
83
- | Metric | pnext | Next.js | Ratio |
84
- | --- | --- | --- | --- |
85
- | Dev cold start (ready) | 57.1 ms | 246.4 ms | 4.31x |
86
- | Dev first page HTML | 127.0 ms | 1285.8 ms | 10.12x |
87
- | Dev warm request (p50 of 7) | 21.6 ms | 34.1 ms | 1.58x |
88
- | Dev server RSS (ready + 7 warm) | 168.6 MB | 577.1 MB | 3.42x |
89
- | HMR save → visible | 300.4 ms | 97.6 ms | 0.32x |
90
- | Prod build (wall) | 637.5 ms | 4566.3 ms | 7.16x |
91
- | Prod build peak RSS | 188.0 MB | 569.0 MB | 3.03x |
92
- | Prod start (ready) | 55.6 ms | 128.8 ms | 2.32x |
93
- | Prod warm request (p50 of 7) | 0.5 ms | 1.7 ms | 3.41x |
94
- | Prod server RSS (ready + 7 warm) | 87.3 MB | 90.6 MB | 1.04x |
95
- | Framework install size | 7.1 MB | 420.7 MB | 59.14x |
96
- | First-page client JS (raw) | 38.51 KB | 507.42 KB | 13.18x |
97
- | First-page client JS (gzip) | 15.26 KB | 144.85 KB | 9.50x |
98
- | First-page JS files | 6 | 9 | 1.50x |
99
-
100
- Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
101
-
102
- ## Client runtime bundles
103
-
104
- | Runtime | Raw | Gzip | Brotli |
105
- | --- | --- | --- | --- |
106
- | router-prefetch-only | 473 B | 348 B | 283 B |
107
- | combined-router-hydrator | 10.47 KB | 4.47 KB | 4.07 KB |
108
-
109
- ## Targets
110
-
111
- | Target | Limit | Measured | Status |
112
- | --- | --- | --- | --- |
113
- | Dev cold start, ssr fixture (pnext) | <= 100 ms | 56.0 ms | PASS |
114
- | Router runtime | <= 1.00 KB gzip | 348 B gzip | PASS |
115
- | Hydrated-route framework tax | <= 5.00 KB gzip | 4.47 KB gzip | PASS |
116
- | Zero-island route client JS (ssr `/about`) | 0 B (core pnext) | 4.46 KB (compat.next) | not exercised |
117
-
118
- ## Notes
119
-
120
- - First-page client JS counts the `<script src>` and `rel="modulepreload"` assets the
121
- route executes, fetched from each framework's own production server; `noModule`
122
- polyfills and speculative `rel="preload"` hints for other routes are excluded on both sides.
123
- - Dev cold start wipes the output directory, spawns the dev server on a free port and
124
- stops at the readiness banner; first page HTML is the GET that follows it, so it
125
- includes on-demand compilation.
126
- - HMR appends a marker to a rendered string in a page component and polls until the
127
- page serves it back. A 200 with stale HTML does not count. pnext applies the save via
128
- live-reload (full document refresh, no client HMR runtime); Next applies it via Fast
129
- Refresh. The metric is model-agnostic: time until fresh content is served.
130
- - Fixtures enable `compat.next` so one source tree runs on both frameworks. That ships
131
- pnext's Next-compat navigation client, which a core pnext app does not carry — the
132
- 0 B zero-island budget is a core-pnext invariant this suite does not exercise.
133
-
134
- ## Memory
135
-
136
- - RSS is summed across the whole process tree (parent + spawned workers), read once at a
137
- fixed point: right after the ready signal and the 7 warm requests, dev and prod alike.
138
- It is never sampled at an arbitrary time, since RSS is pressure-sensitive.
139
- - Build peak RSS comes from `/usr/bin/time` wrapping the build process directly (`-l` on
140
- macOS, `-v` on Linux), not the tree-sum helper — it is the OS-reported peak over the
141
- whole build, not a single snapshot. The framework server-entry prebundle is emitted by
142
- a short-lived child whose transient RSS (~15–25 MB, returned at exit) this number
143
- excludes.
144
- - Framework install size is each framework's own package cost, not the fixture's total
145
- `node_modules`, which both frameworks share: `next` + its platform `@next/swc-*` binary,
146
- or `@wular/pnext`'s npm-publish footprint (its `package.json` "files" list, since this
147
- workspace resolves it to source rather than an installed build) + `preact`.
148
-
149
- ## Commands
150
-
151
- ```sh
152
- bun bench # every fixture, both frameworks
153
- bun bench/run.ts ssr # one fixture
154
- bun bench/run.ts all --framework=pnext --runs=5
155
- ```
31
+ - The fixtures use the same source under both frameworks.
32
+ - Dev first page HTML includes on-demand compilation after a cold server start.
33
+ - Dev memory is summed across the server process tree after readiness and seven warm requests.
34
+ - Build memory is the operating system's reported peak RSS.
@@ -1,127 +1,130 @@
1
1
  # Rendering
2
2
 
3
- pnext renders on the server first and sends browser JavaScript only for interactive components.
3
+ Where a component runs, what crosses between server and browser, and how to stream or defer work. pnext renders on the server first and sends browser JavaScript only for the components that need it.
4
4
 
5
5
  ## Server Components
6
6
 
7
- Pages and layout components are Server Components. They run only on the server, so they can read files, query databases, call internal services, and use server-only dependencies without adding that code to the browser bundle.
8
-
9
- A `layout.tsx` file does not need a default export. Use metadata-only layouts when a segment only needs to set page metadata.
10
-
11
- Use Server Components for data loading and non-interactive UI:
12
-
13
- - Render HTML from server data.
14
- - Keep expensive parsing, formatting, and data libraries on the server.
15
- - Pass serializable props to Client Components for interactive parts.
16
- - Render Client Components inside Server Components when part of the page needs interactivity.
17
- - Read request data from explicit page/layout props when a route depends on headers or cookies.
7
+ Pages and layouts are Server Components. They run only on the server, so they can read files, query databases, call internal services, and use server-only dependencies without any of that reaching the browser bundle. They can be async, and static builds wait for the final HTML.
18
8
 
19
9
  ```tsx
20
10
  export default async function Page() {
21
- const post = await getPost();
22
- return <article>{post.title}</article>;
11
+ const post = await getPost()
12
+ return <article>{post.title}</article>
23
13
  }
24
14
  ```
25
15
 
26
- Server components can be async. Static builds wait for the final HTML.
27
-
28
- Pages and layouts that read `request` receive a `NextRequest` compatible object. It extends the standard Request with convenience access for cookies and headers.
29
-
30
- Use `request.cookies.get()`, `request.headers.get()`, `request.method`, and `request.url` from props. It keeps static routes static and makes request-dependent routes dynamic.
31
-
32
- Apps using `compat.next` can also import `cookies()` and `headers()` from `next/headers`.
16
+ A layout file does not need a default export. Use a metadata-only layout when a segment only needs to set page metadata.
33
17
 
34
- ## Document
18
+ ## Client Components
35
19
 
36
- Use the root `layout.tsx` to own `<html>` and `<body>`:
20
+ A `"use client"` directive marks a component that needs browser APIs, hooks, or event handlers. Server Components can render them. They render HTML on the server, then hydrate in the browser.
37
21
 
38
22
  ```tsx
39
- import type { LayoutProps } from '@wular/pnext';
23
+ 'use client'
40
24
 
41
- export default function Layout({ children }: LayoutProps) {
25
+ import { useState } from 'preact/hooks'
26
+
27
+ export function Counter({ initial }: { initial: number }) {
28
+ const [count, setCount] = useState(initial)
42
29
  return (
43
- <html lang="en">
44
- <body>{children}</body>
45
- </html>
46
- );
30
+ <button onClick={() => setCount(count + 1)}>Count {count}</button>
31
+ )
47
32
  }
48
33
  ```
49
34
 
50
- If the root layout exports a default component, it must return `<html>` and `<body>`.
51
- If the root layout only exports metadata, pnext creates the document shell.
35
+ Files imported by a client component become client code automatically. Add the directive to an imported file only when a server component also renders it directly.
52
36
 
53
- ### Metadata
37
+ A Client Component can wrap server-rendered children. Those children stay server-rendered, and only the wrapper hydrates.
54
38
 
55
- Pages and layouts can export core build-time `metadata`, `metadata()`, `viewport`, and `viewport()`. See [Metadata](./metadata.md) for the full core metadata model.
39
+ Put the directive at the top of a page when the whole page needs browser-only behavior. Prefer smaller client components when only part of the page is interactive.
56
40
 
57
- ## Request Cache
41
+ ## Lazy loading
58
42
 
59
- Use server-only `cache()` to dedupe repeated server work during one render or route-handler request:
43
+ `dynamic()` defers loading a component. It works in Server Components and Client Components alike, and inside a Client Component it is a browser lazy loader. The loader can also be a literal module path for a default export.
60
44
 
61
45
  ```tsx
62
- import { cache } from '@wular/pnext/cache';
46
+ import { dynamic } from '@wular/pnext/dynamic'
63
47
 
64
- export const getPost = cache(async (id: string) => {
65
- return db.post.findUnique({ where: { id } });
66
- });
48
+ const Chart = dynamic(() => import('./chart').then(m => m.Chart), {
49
+ load: 'visible',
50
+ })
67
51
  ```
68
52
 
69
- Calls to the same cached function with the same arguments share the result inside the current request. The cache is cleared between requests, so it does not share data across users, is not a persistent data cache, and should not be imported from Client Components.
53
+ A Client Component target renders on the server and hydrates by default. The options change that:
70
54
 
71
- ## Client Components
55
+ - `ssr: false` produces browser-only output.
56
+ - `load: 'visible'` waits for the island to enter view, and accepts `rootMargin` and `threshold`. It is browser-only as well unless `ssr` is true.
57
+ - `loading` supplies fallback UI.
72
58
 
73
- Add `"use client"` to components that need browser APIs, hooks, or event handlers.
59
+ ## Props that cross the boundary
74
60
 
75
- ```tsx
76
- // app/counter.tsx
77
- 'use client';
61
+ Props passed from a server component to a client component must be serializable.
78
62
 
79
- import { useState } from 'preact/hooks';
63
+ - Accepted: strings, numbers, booleans, `null`, `undefined`, arrays, plain objects, `Map`, `Set`, typed arrays, and `ArrayBuffer`.
64
+ - Rejected: functions, symbols, `bigint`, and class instances such as `Date` and `URL`. Pass those as strings.
80
65
 
81
- export function Counter({ initial }: { initial: number }) {
82
- const [count, setCount] = useState(initial);
83
- return <button onClick={() => setCount(count + 1)}>Count {count}</button>;
84
- }
85
- ```
66
+ Self-referencing values are fine, because they are written as back-references.
86
67
 
87
- ```tsx
88
- // app/page.tsx
89
- import { Counter } from './counter';
68
+ ## Reading request data
90
69
 
91
- export default function Page() {
92
- return <Counter initial={0} />;
93
- }
70
+ The request prop on a page or layout is a `NextRequest` compatible object, which extends the standard Request with convenience access for cookies and headers, plus method and URL.
71
+
72
+ ```tsx
73
+ const theme = request?.cookies.get('theme')?.value
94
74
  ```
95
75
 
96
- Files imported by a client component are client code automatically. Add `"use client"` to an imported file only when it is also rendered directly from a server component.
76
+ Reading it keeps static routes static and marks request-dependent routes dynamic. Apps using `compat.next` can import `cookies()` and `headers()` from `next/headers` instead.
77
+
78
+ ## Streaming
97
79
 
98
- Props passed from server components to client components must be serializable. pnext accepts strings, numbers, booleans, `null`, `undefined`, arrays, plain objects, `Map`, `Set`, typed arrays, and `ArrayBuffer`. Self-referencing values are fine — they are written as back-references. It rejects functions, symbols, `bigint`, and class instances, including `Date` and `URL`; pass those as strings.
80
+ A Suspense boundary sends fallback HTML while the server finishes the content inside it.
99
81
 
100
- Server Components can render Client Components. Client Components render HTML on the server, then hydrate in the browser. Client Components can wrap server-rendered children; those children stay server-rendered and only the wrapper hydrates in the browser.
82
+ ```tsx
83
+ <Suspense fallback={<p>Loading comments...</p>}>
84
+ <Comments />
85
+ </Suspense>
86
+ ```
101
87
 
102
- Use `dynamic({ ssr: false })` for browser-only Client Components. `dynamic({ load: 'visible' })` is browser-only by default, so it can keep Preact and the component chunk out of the initial route bundle until the island enters view.
88
+ Dev and server responses stream the fallback first, then replace it when the content is ready. Static builds wait for the final content.
103
89
 
104
- If the whole page needs browser-only behavior, put `"use client"` at the top of the page. Prefer smaller client components when only part of the page is interactive.
90
+ pnext exports `Suspense` and `SuspenseProps` from `@wular/pnext` and resolves these boundaries in its own renderer, so core apps need nothing from `preact/compat`. Apps with `compat.react` can keep importing React's `Suspense`, since both identities are recognized.
105
91
 
106
- ## Streaming
92
+ A segment `loading` file creates one of these boundaries automatically. See [Routing](./routing.md#convention-files).
107
93
 
108
- Wrap slower server content in `<Suspense>` to show fallback HTML while the server finishes rendering the rest. The pnext renderer resolves these boundaries itself, so core apps use pnext's own `Suspense` — no `preact/compat` involved. Apps with `compat.react` can keep importing React's `Suspense`; both identities are recognized.
94
+ ## Request cache
95
+
96
+ `cache()` dedupes repeated work inside one render or route-handler request.
109
97
 
110
98
  ```tsx
111
- import { Suspense } from '@wular/pnext';
99
+ import { cache } from '@wular/pnext/cache'
112
100
 
113
- async function Comments() {
114
- const comments = await getComments();
115
- return <CommentList comments={comments} />;
116
- }
101
+ export const getPost = cache(async (id: string) =>
102
+ db.post.findUnique({ where: { id } }),
103
+ )
104
+ ```
105
+
106
+ Calls with the same arguments share one result. The cache is cleared between requests, so it never shares data across users. It is not a persistent data cache, and it should not be imported from Client Components.
107
+
108
+ ## Root layout
117
109
 
118
- export default function Page() {
110
+ The root layout owns the document. One with a default component must return `<html>` and `<body>`. One that only exports metadata gets a document shell from pnext. For the head itself, see [Metadata](./metadata.md).
111
+
112
+ ```tsx
113
+ import type { LayoutProps } from '@wular/pnext'
114
+
115
+ export default function Layout({ children }: LayoutProps) {
119
116
  return (
120
- <Suspense fallback={<p>Loading comments...</p>}>
121
- <Comments />
122
- </Suspense>
123
- );
117
+ <html lang="en">
118
+ <body>{children}</body>
119
+ </html>
120
+ )
124
121
  }
125
122
  ```
126
123
 
127
- Dev and server responses can stream the fallback first, then replace it when the server content is ready. Static builds wait for the final content.
124
+ ## Experimental: partial prerendering and cacheComponents
125
+
126
+ A page that uses request data and exports `experimental_ppr` produces a static shell with postponed Suspense content that is resumed for the request. The renderer represents postponed work with a `PostponeError`, so wrap that content in Suspense rather than handling the error yourself.
127
+
128
+ `cacheComponents` requires `compat.next` and is set in the loaded Next configuration. It makes the shell-and-resume path the default, and turns request APIs into values that postpone when first awaited inside Suspense during prerendering: `cookies()`, `headers()`, `connection()`, route params, search params, and `io()`. The `io()` helper comes from the compat API and has no effect unless cacheComponents is enabled.
129
+
130
+ Cache lifetimes and cache tags recorded during prerendering are honored when prebuilt output is served, including partial shells. Reach them through the Next `cacheLife()` and cache-tag APIs, which compat supplies.