@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.
- package/README.md +22 -16
- package/bin/pnext +6 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +87 -22
- package/reference/config.md +40 -93
- package/reference/css.md +42 -33
- package/reference/dev.md +26 -33
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +31 -46
- package/reference/navigation.md +65 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +78 -75
- package/reference/routing.md +97 -107
- package/reference/typegen.md +27 -29
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +121 -114
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +52 -53
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +72 -64
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +94 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +802 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +32 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +2022 -1995
- package/src/{api → client}/router/types.ts +98 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1897
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +86 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2087 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +318 -296
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +87 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +827 -815
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +954 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +428 -406
- package/src/styles.d.ts +9 -0
- package/src/types.ts +324 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/reference/overview.md +0 -35
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
package/reference/navigation.md
CHANGED
|
@@ -1,149 +1,109 @@
|
|
|
1
1
|
# Navigation
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
##
|
|
37
|
+
## Prefetching
|
|
51
38
|
|
|
52
|
-
|
|
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
|
-
`
|
|
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
|
-
- `
|
|
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
|
-
|
|
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
|
-
##
|
|
50
|
+
## Soft navigation
|
|
64
51
|
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
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
|
-
|
|
67
|
+
The status is 307 unless you pass 303, 307, or 308. `permanentRedirect()` returns 308.
|
|
84
68
|
|
|
85
|
-
|
|
86
|
-
import { href, redirect } from '@wular/pnext/navigation';
|
|
69
|
+
`notFound()` renders the nearest `not-found` fallback with a 404 response.
|
|
87
70
|
|
|
88
|
-
redirect(
|
|
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
|
-
##
|
|
73
|
+
## href()
|
|
92
74
|
|
|
93
|
-
|
|
75
|
+
Builds a typed URL outside JSX. Routes without params take no params object.
|
|
94
76
|
|
|
95
|
-
```
|
|
96
|
-
'
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
+
Exported from `@wular/pnext/navigation/client`, for use in components marked `"use client"`.
|
|
128
87
|
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
import { dynamic } from '@wular/pnext/dynamic';
|
|
99
|
+
`useSearchParams()` returns a `ReadonlyURLSearchParams`, whose mutation methods throw. Copy it before changing it.
|
|
135
100
|
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
107
|
+
## Auth interrupts
|
|
148
108
|
|
|
149
|
-
|
|
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).
|
package/reference/performance.md
CHANGED
|
@@ -1,155 +1,34 @@
|
|
|
1
1
|
# Performance
|
|
2
2
|
|
|
3
|
-
Measured against Next.js
|
|
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
|
-
|
|
|
6
|
-
|
|
|
7
|
-
| Dev
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
| Production
|
|
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.1–12.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.2–8.8× faster** |
|
|
10
|
+
| Dev server memory | **3.4–4.1× less** |
|
|
11
|
+
| Production cold start (ready) | **1.8–2.3× faster** |
|
|
18
12
|
|
|
19
|
-
|
|
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
|
-
|
|
15
|
+
## What ships to the browser
|
|
25
16
|
|
|
26
|
-
|
|
17
|
+
Server-only routes ship 0 KB of route JavaScript, because they need no browser runtime to render their content.
|
|
27
18
|
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
23
|
+
## The HMR result
|
|
38
24
|
|
|
39
|
-
|
|
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
|
-
|
|
27
|
+
## How these were measured
|
|
58
28
|
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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.
|
package/reference/rendering.md
CHANGED
|
@@ -1,127 +1,130 @@
|
|
|
1
1
|
# Rendering
|
|
2
2
|
|
|
3
|
-
pnext renders on the server first and sends browser JavaScript only for
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
18
|
+
## Client Components
|
|
35
19
|
|
|
36
|
-
|
|
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
|
-
|
|
23
|
+
'use client'
|
|
40
24
|
|
|
41
|
-
|
|
25
|
+
import { useState } from 'preact/hooks'
|
|
26
|
+
|
|
27
|
+
export function Counter({ initial }: { initial: number }) {
|
|
28
|
+
const [count, setCount] = useState(initial)
|
|
42
29
|
return (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
</html>
|
|
46
|
-
);
|
|
30
|
+
<button onClick={() => setCount(count + 1)}>Count {count}</button>
|
|
31
|
+
)
|
|
47
32
|
}
|
|
48
33
|
```
|
|
49
34
|
|
|
50
|
-
|
|
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
|
-
|
|
37
|
+
A Client Component can wrap server-rendered children. Those children stay server-rendered, and only the wrapper hydrates.
|
|
54
38
|
|
|
55
|
-
|
|
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
|
-
##
|
|
41
|
+
## Lazy loading
|
|
58
42
|
|
|
59
|
-
|
|
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 {
|
|
46
|
+
import { dynamic } from '@wular/pnext/dynamic'
|
|
63
47
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
48
|
+
const Chart = dynamic(() => import('./chart').then(m => m.Chart), {
|
|
49
|
+
load: 'visible',
|
|
50
|
+
})
|
|
67
51
|
```
|
|
68
52
|
|
|
69
|
-
|
|
53
|
+
A Client Component target renders on the server and hydrates by default. The options change that:
|
|
70
54
|
|
|
71
|
-
|
|
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
|
-
|
|
59
|
+
## Props that cross the boundary
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
// app/counter.tsx
|
|
77
|
-
'use client';
|
|
61
|
+
Props passed from a server component to a client component must be serializable.
|
|
78
62
|
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
// app/page.tsx
|
|
89
|
-
import { Counter } from './counter';
|
|
68
|
+
## Reading request data
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
+
A Suspense boundary sends fallback HTML while the server finishes the content inside it.
|
|
99
81
|
|
|
100
|
-
|
|
82
|
+
```tsx
|
|
83
|
+
<Suspense fallback={<p>Loading comments...</p>}>
|
|
84
|
+
<Comments />
|
|
85
|
+
</Suspense>
|
|
86
|
+
```
|
|
101
87
|
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
+
A segment `loading` file creates one of these boundaries automatically. See [Routing](./routing.md#convention-files).
|
|
107
93
|
|
|
108
|
-
|
|
94
|
+
## Request cache
|
|
95
|
+
|
|
96
|
+
`cache()` dedupes repeated work inside one render or route-handler request.
|
|
109
97
|
|
|
110
98
|
```tsx
|
|
111
|
-
import {
|
|
99
|
+
import { cache } from '@wular/pnext/cache'
|
|
112
100
|
|
|
113
|
-
async
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
121
|
-
<
|
|
122
|
-
</
|
|
123
|
-
)
|
|
117
|
+
<html lang="en">
|
|
118
|
+
<body>{children}</body>
|
|
119
|
+
</html>
|
|
120
|
+
)
|
|
124
121
|
}
|
|
125
122
|
```
|
|
126
123
|
|
|
127
|
-
|
|
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.
|