0
# Static Rendering
1
2
Static pre-rendering capabilities for React Server Components, enabling build-time generation of server component output for improved performance and SEO.
3
4
## Capabilities
5
6
### Unstable Prerender
7
8
Pre-renders React Server Components at build time, generating static output that can be served directly or hydrated on the client.
9
10
```javascript { .api }
11
/**
12
* Pre-renders server components for static generation
13
* @param model - React server component tree to pre-render
14
* @param webpackMap - Client manifest for module resolution
15
* @param options - Optional rendering configuration
16
* @returns Promise resolving to pre-render result
17
*/
18
function unstable_prerender(
19
model: ReactClientValue,
20
webpackMap: ClientManifest,
21
options?: ServerOptions
22
): Promise<StaticResult>;
23
```
24
25
**Usage Examples:**
26
27
```javascript
28
import { unstable_prerender } from "react-server-dom-webpack/static.browser";
29
30
// Pre-render a server component at build time
31
async function generateStaticPage() {
32
const clientManifest = JSON.parse(
33
await fs.readFile("./react-client-manifest.json", "utf8")
34
);
35
36
const result = unstable_prerender(
37
<HomePage posts={staticPosts} />,
38
clientManifest,
39
{
40
identifierPrefix: "static-",
41
onError: (error) => {
42
console.error("Static render error:", error);
43
}
44
}
45
);
46
47
// Save static output
48
await fs.writeFile("./dist/home.html", result.prelude);
49
return result;
50
}
51
52
// Build-time page generation
53
const pages = [
54
{ path: "/", component: <HomePage /> },
55
{ path: "/about", component: <AboutPage /> },
56
{ path: "/contact", component: <ContactPage /> }
57
];
58
59
for (const page of pages) {
60
const result = unstable_prerender(page.component, clientManifest);
61
await saveStaticPage(page.path, result);
62
}
63
```
64
65
### Node.js Pre-rendering
66
67
Node.js-specific pre-rendering with stream support for build pipelines.
68
69
```javascript { .api }
70
/**
71
* Pre-renders server components to Node.js stream for build tools
72
* @param model - React server component tree to pre-render
73
* @param webpackMap - Client manifest for module resolution
74
* @param options - Optional rendering configuration
75
* @returns ReadableStream containing pre-rendered content
76
*/
77
function unstable_prerenderToNodeStream(
78
model: ReactClientValue,
79
webpackMap: ClientManifest,
80
options?: ServerOptions
81
): ReadableStream;
82
```
83
84
**Usage Examples:**
85
86
```javascript
87
import { unstable_prerenderToNodeStream } from "react-server-dom-webpack/static.node";
88
import { createWriteStream } from "fs";
89
90
// Stream pre-rendered content to file
91
async function prerenderToFile(component, outputPath) {
92
const clientManifest = JSON.parse(
93
fs.readFileSync("./react-client-manifest.json", "utf8")
94
);
95
96
const stream = unstable_prerenderToNodeStream(
97
component,
98
clientManifest,
99
{
100
onError: (error) => console.error("Prerender error:", error),
101
signal: abortController.signal
102
}
103
);
104
105
const writeStream = createWriteStream(outputPath);
106
107
// Pipe pre-rendered output to file
108
stream.pipe(writeStream);
109
110
return new Promise((resolve, reject) => {
111
writeStream.on("finish", resolve);
112
writeStream.on("error", reject);
113
});
114
}
115
116
// Build-time static generation
117
await prerenderToFile(<BlogPost post={postData} />, "./dist/blog/post-123.html");
118
```
119
120
### Static Site Generation Patterns
121
122
Common patterns for integrating static pre-rendering into build workflows.
123
124
#### Build-Time Page Generation
125
126
```javascript
127
// build/generatePages.js
128
import { unstable_prerender } from "react-server-dom-webpack/static.node";
129
import { loadClientManifest, getAllPosts, getAllPages } from "./utils";
130
131
async function generateStaticSite() {
132
const clientManifest = await loadClientManifest();
133
const posts = await getAllPosts();
134
const pages = await getAllPages();
135
136
// Generate static pages
137
for (const page of pages) {
138
const result = unstable_prerender(
139
<Page {...page} />,
140
clientManifest,
141
{ identifierPrefix: `page-${page.id}-` }
142
);
143
144
await fs.writeFile(`./dist/${page.slug}.html`, result.prelude);
145
console.log(`Generated: ${page.slug}`);
146
}
147
148
// Generate blog posts
149
for (const post of posts) {
150
const result = unstable_prerender(
151
<BlogPost post={post} />,
152
clientManifest,
153
{ identifierPrefix: `post-${post.id}-` }
154
);
155
156
await fs.writeFile(`./dist/blog/${post.slug}.html`, result.prelude);
157
console.log(`Generated: blog/${post.slug}`);
158
}
159
}
160
161
generateStaticSite().catch(console.error);
162
```
163
164
#### Incremental Static Regeneration
165
166
```javascript
167
// api/revalidate.js
168
import { unstable_prerender } from "react-server-dom-webpack/static.browser";
169
170
export async function POST(request) {
171
const { path, data } = await request.json();
172
173
try {
174
// Re-generate specific page
175
const result = unstable_prerender(
176
<DynamicPage data={data} />,
177
clientManifest,
178
{
179
identifierPrefix: `revalidate-${Date.now()}-`,
180
onError: (error) => {
181
console.error(`Revalidation error for ${path}:`, error);
182
}
183
}
184
);
185
186
// Update static file
187
await fs.writeFile(`./dist${path}.html`, result.prelude);
188
189
return Response.json({ success: true, path });
190
} catch (error) {
191
return Response.json({ error: error.message }, { status: 500 });
192
}
193
}
194
```
195
196
#### Build Tool Integration
197
198
```javascript
199
// webpack.config.js
200
const ReactFlightWebpackPlugin = require("react-server-dom-webpack/plugin");
201
202
module.exports = {
203
plugins: [
204
new ReactFlightWebpackPlugin({
205
isServer: false,
206
clientReferences: ["./src/**/*.client.js"]
207
}),
208
209
// Custom plugin for static generation
210
{
211
apply(compiler) {
212
compiler.hooks.afterEmit.tapAsync("StaticGeneration", async (compilation, callback) => {
213
const { unstable_prerender } = require("react-server-dom-webpack/static.node");
214
215
// Generate static pages after build
216
await generateStaticPages(unstable_prerender);
217
callback();
218
});
219
}
220
}
221
]
222
};
223
```
224
225
### Pre-render Result Handling
226
227
Working with pre-render results and their metadata.
228
229
```javascript
230
// Handle pre-render result
231
const result = unstable_prerender(<App />, clientManifest);
232
233
// Extract different parts of the result
234
const {
235
prelude, // Static HTML content
236
postponed, // Postponed content for streaming
237
metadata // Additional rendering metadata
238
} = result;
239
240
// Save static content
241
await fs.writeFile("./static/app.html", prelude);
242
243
// Handle postponed content if any
244
if (postponed) {
245
await handlePostponedContent(postponed);
246
}
247
```
248
249
### Error Handling in Static Rendering
250
251
Comprehensive error handling for build-time rendering issues.
252
253
```javascript
254
import { unstable_prerender } from "react-server-dom-webpack/static.browser";
255
256
async function safePrerender(component, clientManifest, options = {}) {
257
const errors = [];
258
259
const result = unstable_prerender(
260
component,
261
clientManifest,
262
{
263
...options,
264
onError: (error) => {
265
errors.push({
266
message: error.message,
267
stack: error.stack,
268
timestamp: new Date().toISOString()
269
});
270
271
// Log error but continue rendering
272
console.error("Pre-render error:", error);
273
}
274
}
275
);
276
277
return {
278
...result,
279
errors,
280
hasErrors: errors.length > 0
281
};
282
}
283
284
// Use with error reporting
285
const result = await safePrerender(<HomePage />, clientManifest);
286
287
if (result.hasErrors) {
288
await reportBuildErrors(result.errors);
289
290
// Decide whether to fail build or use partial result
291
if (result.errors.some(e => e.critical)) {
292
throw new Error("Critical pre-render errors detected");
293
}
294
}
295
```
296
297
### Performance Optimization
298
299
Optimizing static rendering for large sites and complex components.
300
301
#### Parallel Pre-rendering
302
303
```javascript
304
import { unstable_prerender } from "react-server-dom-webpack/static.node";
305
import pLimit from "p-limit";
306
307
// Limit concurrent pre-rendering operations
308
const limit = pLimit(4);
309
310
async function prerenderPages(pages, clientManifest) {
311
const tasks = pages.map(page =>
312
limit(async () => {
313
const result = unstable_prerender(
314
<Page {...page} />,
315
clientManifest,
316
{ identifierPrefix: `page-${page.id}-` }
317
);
318
319
await fs.writeFile(`./dist/${page.slug}.html`, result.prelude);
320
return { page: page.slug, success: true };
321
})
322
);
323
324
const results = await Promise.allSettled(tasks);
325
326
const successful = results.filter(r => r.status === "fulfilled").length;
327
const failed = results.filter(r => r.status === "rejected").length;
328
329
console.log(`Pre-rendered ${successful} pages, ${failed} failed`);
330
331
return results;
332
}
333
```
334
335
#### Caching Pre-rendered Content
336
337
```javascript
338
// build/cache.js
339
import crypto from "crypto";
340
341
const prerenderCache = new Map();
342
343
function getCacheKey(component, manifest) {
344
const componentHash = crypto
345
.createHash("md5")
346
.update(JSON.stringify(component))
347
.digest("hex");
348
349
const manifestHash = crypto
350
.createHash("md5")
351
.update(JSON.stringify(manifest))
352
.digest("hex");
353
354
return `${componentHash}-${manifestHash}`;
355
}
356
357
async function cachedPrerender(component, clientManifest) {
358
const cacheKey = getCacheKey(component, clientManifest);
359
360
if (prerenderCache.has(cacheKey)) {
361
console.log("Cache hit for pre-render");
362
return prerenderCache.get(cacheKey);
363
}
364
365
const result = unstable_prerender(component, clientManifest);
366
prerenderCache.set(cacheKey, result);
367
368
return result;
369
}
370
```
371
372
## Types
373
374
```javascript { .api }
375
interface StaticResult {
376
/** ReadableStream containing pre-rendered content */
377
prelude: ReadableStream;
378
}
379
380
type ReactClientValue = any;
381
type ClientManifest = Record<string, ImportManifestEntry>;
382
383
interface ImportManifestEntry {
384
id: string;
385
chunks: string[];
386
name: string;
387
}
388
```
389
390
**Note:** Static rendering functions use the same `ServerOptions` interface as documented in [Server APIs](./server-apis.md).
391
392
## Build Integration Notes
393
394
### Webpack Integration
395
396
Static rendering works best with proper Webpack configuration:
397
398
```javascript
399
// webpack.static.config.js
400
module.exports = {
401
target: "node",
402
entry: "./build/static-generator.js",
403
plugins: [
404
new ReactFlightWebpackPlugin({
405
isServer: false,
406
clientReferences: ["./src/**/*.client.js"]
407
})
408
],
409
externals: {
410
// Externalize Node.js modules for static generation
411
"fs": "commonjs fs",
412
"path": "commonjs path"
413
}
414
};
415
```
416
417
### Framework Integration
418
419
Integration with popular static site generators:
420
421
- **Next.js**: Use in `getStaticProps` or build scripts
422
- **Gatsby**: Integrate with `createPages` API
423
- **Vite**: Use in build plugins or custom generators
424
- **Custom**: Create build-time generation scripts
425
426
### SEO and Performance
427
428
Static pre-rendering provides:
429
430
- **Improved SEO**: Search engines can crawl static HTML
431
- **Faster Initial Load**: No client-side rendering delay
432
- **Better Core Web Vitals**: Reduced Time to First Byte
433
- **Progressive Enhancement**: Works without JavaScript