@wular/pnext 0.0.6 → 0.0.8
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 +12 -6
- package/bin/pnext +5 -0
- package/package.json +1 -1
- package/reference/compat.md +86 -21
- package/reference/config.md +40 -93
- package/reference/css.md +40 -35
- package/reference/dev.md +25 -32
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +26 -41
- package/reference/navigation.md +58 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +73 -70
- package/reference/routing.md +87 -97
- package/reference/typegen.md +21 -27
- package/src/api/client-navigation.ts +11 -8
- package/src/api/link.tsx +5 -3
- package/src/cli/analyze.ts +103 -48
- package/src/cli/create.ts +1 -1
- package/src/cli/index.ts +6 -0
- package/src/client/build.ts +10 -2
- package/src/client/entry.ts +13 -9
- package/src/client/router/events.ts +15 -0
- package/src/client/router/runtime.ts +40 -9
- package/src/client/router/types.ts +5 -0
- package/src/config.ts +18 -1
- package/src/dev/server.ts +2 -2
- package/src/render/renderer.ts +11 -0
- package/src/render/static-slots-revive.ts +65 -0
- package/src/render/static-slots.ts +3 -54
- package/src/routing/href.ts +19 -1
- package/src/runtime/loader.ts +3 -1
- package/src/runtime/vendor.ts +5 -4
- package/src/types.ts +5 -1
- package/reference/overview.md +0 -35
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 the
|
|
147
|
-
repo 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,20 +1,10 @@
|
|
|
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() {
|
|
@@ -23,105 +13,118 @@ export default async function Page() {
|
|
|
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.
|
|
16
|
+
A layout file does not need a default export. Use a metadata-only layout when a segment only needs to set page metadata.
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Apps using `compat.next` can also import `cookies()` and `headers()` from `next/headers`.
|
|
33
|
-
|
|
34
|
-
## Document
|
|
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
|
-
<body>{children}</body>
|
|
45
|
-
</html>
|
|
30
|
+
<button onClick={() => setCount(count + 1)}>Count {count}</button>
|
|
46
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
|
-
|
|
48
|
+
const Chart = dynamic(() => import('./chart').then(m => m.Chart), {
|
|
49
|
+
load: 'visible',
|
|
66
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.
|
|
97
77
|
|
|
98
|
-
|
|
78
|
+
## Streaming
|
|
99
79
|
|
|
100
|
-
|
|
80
|
+
A Suspense boundary sends fallback HTML while the server finishes the content inside it.
|
|
101
81
|
|
|
102
|
-
|
|
82
|
+
```tsx
|
|
83
|
+
<Suspense fallback={<p>Loading comments...</p>}>
|
|
84
|
+
<Comments />
|
|
85
|
+
</Suspense>
|
|
86
|
+
```
|
|
103
87
|
|
|
104
|
-
|
|
88
|
+
Dev and server responses stream the fallback first, then replace it when the content is ready. Static builds wait for the final content.
|
|
105
89
|
|
|
106
|
-
|
|
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.
|
|
91
|
+
|
|
92
|
+
A segment `loading` file creates one of these boundaries automatically. See [Routing](./routing.md#convention-files).
|
|
93
|
+
|
|
94
|
+
## Request cache
|
|
107
95
|
|
|
108
|
-
|
|
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
|
+
```
|
|
117
105
|
|
|
118
|
-
|
|
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
|
|
109
|
+
|
|
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
|
-
</
|
|
117
|
+
<html lang="en">
|
|
118
|
+
<body>{children}</body>
|
|
119
|
+
</html>
|
|
123
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.
|