0
# Web Streams Support
1
2
Modern Web Streams API support for serverless environments, edge computing platforms, and modern web standards. These functions enable Vue SSR in environments like CloudFlare Workers, Vercel Edge Functions, and other platforms that support Web Streams instead of Node.js streams.
3
4
## Capabilities
5
6
### renderToWebStream
7
8
Creates a Web ReadableStream that outputs the rendered HTML. This function provides native Web Streams API integration for modern web platforms and edge environments.
9
10
```typescript { .api }
11
/**
12
* Renders input as a Web ReadableStream
13
* @param input - Vue application instance or VNode to render
14
* @param context - Optional SSR context for teleports and additional data
15
* @returns Web ReadableStream containing the rendered HTML
16
* @throws Error if ReadableStream constructor is not available in global scope
17
*/
18
function renderToWebStream(
19
input: App | VNode,
20
context?: SSRContext
21
): ReadableStream;
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import { createSSRApp } from "vue";
28
import { renderToWebStream } from "@vue/server-renderer";
29
30
const app = createSSRApp({
31
template: `
32
<div>
33
<h1>Web Streams</h1>
34
<p>Content streamed via Web ReadableStream</p>
35
</div>
36
`,
37
});
38
39
// Direct usage with Web Response
40
const stream = renderToWebStream(app);
41
const response = new Response(stream, {
42
headers: { 'Content-Type': 'text/html' }
43
});
44
45
// CloudFlare Workers example
46
export default {
47
fetch(request: Request): Response {
48
const vueApp = createSSRApp({
49
template: `<div>Hello from CloudFlare Workers!</div>`
50
});
51
52
const stream = renderToWebStream(vueApp);
53
return new Response(stream, {
54
headers: {
55
'Content-Type': 'text/html',
56
'Cache-Control': 'public, max-age=300'
57
}
58
});
59
}
60
};
61
62
// Vercel Edge Function example
63
export const config = { runtime: 'edge' };
64
65
export default function handler(request: Request): Response {
66
const vueApp = createSSRApp({
67
template: `<div>Hello from Vercel Edge!</div>`
68
});
69
70
return new Response(renderToWebStream(vueApp), {
71
headers: { 'Content-Type': 'text/html' }
72
});
73
}
74
```
75
76
### pipeToWebWritable
77
78
Pipes the rendered output directly to an existing Web WritableStream. This provides more control over the destination and allows for custom stream processing.
79
80
```typescript { .api }
81
/**
82
* Render and pipe to an existing Web WritableStream instance
83
* @param input - Vue application instance or VNode to render
84
* @param context - Optional SSR context for teleports and additional data
85
* @param writable - Web WritableStream to pipe the output to
86
*/
87
function pipeToWebWritable(
88
input: App | VNode,
89
context?: SSRContext,
90
writable: WritableStream
91
): void;
92
```
93
94
**Usage Examples:**
95
96
```typescript
97
import { createSSRApp } from "vue";
98
import { pipeToWebWritable } from "@vue/server-renderer";
99
100
const app = createSSRApp({
101
template: `
102
<div>
103
<h1>Piped Web Stream</h1>
104
<p>This content is piped to a WritableStream</p>
105
</div>
106
`,
107
});
108
109
// Using TransformStream for processing
110
const { readable, writable } = new TransformStream();
111
112
// Pipe Vue content to the writable side
113
pipeToWebWritable(app, {}, writable);
114
115
// Process the readable side
116
const response = new Response(readable, {
117
headers: { 'Content-Type': 'text/html' }
118
});
119
120
// Custom WritableStream implementation
121
class CustomWriter {
122
constructor() {
123
this.chunks = [];
124
}
125
126
write(chunk: Uint8Array): Promise<void> {
127
this.chunks.push(chunk);
128
return Promise.resolve();
129
}
130
131
close(): Promise<void> {
132
const content = new TextDecoder().decode(
133
new Uint8Array(this.chunks.flat())
134
);
135
console.log('Final content:', content);
136
return Promise.resolve();
137
}
138
139
abort(reason: any): Promise<void> {
140
console.error('Stream aborted:', reason);
141
return Promise.resolve();
142
}
143
}
144
145
const customWritable = new WritableStream(new CustomWriter());
146
pipeToWebWritable(app, {}, customWritable);
147
```
148
149
### SSR Context with Web Streams
150
151
Both functions support SSR context for teleports and custom data:
152
153
```typescript
154
import { createSSRApp } from "vue";
155
import { renderToWebStream } from "@vue/server-renderer";
156
157
const app = createSSRApp({
158
template: `
159
<div>
160
<h1>Main Content</h1>
161
<Teleport to="#modal">
162
<div class="modal">Modal content</div>
163
</Teleport>
164
</div>
165
`,
166
});
167
168
// CloudFlare Workers with teleports
169
export default {
170
async fetch(request: Request): Promise<Response> {
171
const context = {
172
url: request.url,
173
userAgent: request.headers.get('User-Agent'),
174
};
175
176
const { readable, writable } = new TransformStream();
177
178
// Start rendering (non-blocking)
179
pipeToWebWritable(app, context, writable);
180
181
// Create full HTML response with teleports
182
const reader = readable.getReader();
183
const encoder = new TextEncoder();
184
185
return new Response(
186
new ReadableStream({
187
async start(controller) {
188
// Send HTML document start
189
controller.enqueue(encoder.encode('<!DOCTYPE html><html><body>'));
190
191
// Stream main content
192
try {
193
while (true) {
194
const { done, value } = await reader.read();
195
if (done) break;
196
controller.enqueue(value);
197
}
198
199
// Add teleported content
200
if (context.teleports) {
201
for (const [target, content] of Object.entries(context.teleports)) {
202
const teleportHtml = `<div id="${target.slice(1)}">${content}</div>`;
203
controller.enqueue(encoder.encode(teleportHtml));
204
}
205
}
206
207
// Close HTML document
208
controller.enqueue(encoder.encode('</body></html>'));
209
controller.close();
210
} catch (error) {
211
controller.error(error);
212
}
213
}
214
}),
215
{
216
headers: { 'Content-Type': 'text/html' }
217
}
218
);
219
}
220
};
221
```
222
223
## Platform Integration Examples
224
225
### CloudFlare Workers
226
227
```typescript
228
// worker.ts
229
import { createSSRApp } from "vue";
230
import { renderToWebStream } from "@vue/server-renderer";
231
232
export default {
233
async fetch(request: Request, env: any, ctx: any): Promise<Response> {
234
// Parse URL for routing
235
const url = new URL(request.url);
236
237
const app = createSSRApp({
238
setup() {
239
return { path: url.pathname };
240
},
241
template: `
242
<div>
243
<h1>CloudFlare Workers + Vue SSR</h1>
244
<p>Current path: {{ path }}</p>
245
<p>Rendered at: ${new Date().toISOString()}</p>
246
</div>
247
`
248
});
249
250
const stream = renderToWebStream(app, {
251
requestUrl: request.url,
252
timestamp: Date.now()
253
});
254
255
return new Response(stream, {
256
headers: {
257
'Content-Type': 'text/html',
258
'X-Powered-By': 'CloudFlare Workers + Vue'
259
}
260
});
261
}
262
};
263
```
264
265
### Vercel Edge Functions
266
267
```typescript
268
// pages/api/ssr.ts
269
import { createSSRApp } from "vue";
270
import { renderToWebStream } from "@vue/server-renderer";
271
272
export const config = {
273
runtime: 'edge',
274
};
275
276
export default async function handler(request: Request): Promise<Response> {
277
const { searchParams } = new URL(request.url);
278
const name = searchParams.get('name') || 'World';
279
280
const app = createSSRApp({
281
setup() {
282
return { name };
283
},
284
template: `
285
<div>
286
<h1>Hello {{ name }}!</h1>
287
<p>Rendered with Vercel Edge Functions</p>
288
</div>
289
`
290
});
291
292
const stream = renderToWebStream(app);
293
294
return new Response(stream, {
295
headers: {
296
'Content-Type': 'text/html',
297
'Cache-Control': 'public, s-maxage=60'
298
}
299
});
300
}
301
```
302
303
### Deno Deploy
304
305
```typescript
306
// main.ts
307
import { createSSRApp } from "vue";
308
import { renderToWebStream } from "@vue/server-renderer";
309
310
Deno.serve(async (request: Request): Promise<Response> => {
311
const app = createSSRApp({
312
template: `
313
<div>
314
<h1>Deno Deploy + Vue SSR</h1>
315
<p>Fast edge rendering with Deno</p>
316
</div>
317
`
318
});
319
320
const stream = renderToWebStream(app);
321
322
return new Response(stream, {
323
headers: {
324
'Content-Type': 'text/html',
325
'X-Powered-By': 'Deno Deploy'
326
}
327
});
328
});
329
```
330
331
## Advanced Usage
332
333
### Stream Processing with TransformStream
334
335
```typescript
336
import { renderToWebStream } from "@vue/server-renderer";
337
338
class HTMLWrapperTransform {
339
constructor() {
340
this.headerSent = false;
341
}
342
343
transform(chunk: Uint8Array, controller: TransformStreamDefaultController) {
344
if (!this.headerSent) {
345
// Add HTML document wrapper
346
const header = new TextEncoder().encode(
347
'<!DOCTYPE html><html><head><title>My App</title></head><body>'
348
);
349
controller.enqueue(header);
350
this.headerSent = true;
351
}
352
353
controller.enqueue(chunk);
354
}
355
356
flush(controller: TransformStreamDefaultController) {
357
// Close HTML document
358
const footer = new TextEncoder().encode('</body></html>');
359
controller.enqueue(footer);
360
}
361
}
362
363
const app = createSSRApp(/* your app */);
364
const vueStream = renderToWebStream(app);
365
366
const { readable, writable } = new TransformStream(new HTMLWrapperTransform());
367
368
// Pipe Vue stream through transformer
369
vueStream.pipeTo(writable);
370
371
// Return processed stream
372
return new Response(readable, {
373
headers: { 'Content-Type': 'text/html' }
374
});
375
```
376
377
### Error Handling
378
379
```typescript
380
import { renderToWebStream } from "@vue/server-renderer";
381
382
export default {
383
async fetch(request: Request): Promise<Response> {
384
try {
385
const app = createSSRApp({
386
setup() {
387
// Simulate potential error
388
const shouldError = new URL(request.url).searchParams.has('error');
389
if (shouldError) {
390
throw new Error('Component error');
391
}
392
return {};
393
},
394
template: '<div>Success!</div>'
395
});
396
397
const stream = renderToWebStream(app);
398
return new Response(stream, {
399
headers: { 'Content-Type': 'text/html' }
400
});
401
} catch (error) {
402
// Return error page
403
const errorStream = renderToWebStream(createSSRApp({
404
template: `<div>Error: ${error.message}</div>`
405
}));
406
407
return new Response(errorStream, {
408
status: 500,
409
headers: { 'Content-Type': 'text/html' }
410
});
411
}
412
}
413
};
414
```
415
416
### Performance Monitoring
417
418
```typescript
419
import { renderToWebStream } from "@vue/server-renderer";
420
421
class PerformanceMonitoringTransform {
422
constructor() {
423
this.startTime = Date.now();
424
this.chunkCount = 0;
425
}
426
427
transform(chunk: Uint8Array, controller: TransformStreamDefaultController) {
428
this.chunkCount++;
429
430
if (this.chunkCount === 1) {
431
const ttfb = Date.now() - this.startTime;
432
console.log(`Time to first byte: ${ttfb}ms`);
433
}
434
435
controller.enqueue(chunk);
436
}
437
438
flush(controller: TransformStreamDefaultController) {
439
const totalTime = Date.now() - this.startTime;
440
console.log(`Total render time: ${totalTime}ms, chunks: ${this.chunkCount}`);
441
}
442
}
443
444
const app = createSSRApp(/* your app */);
445
const vueStream = renderToWebStream(app);
446
const { readable, writable } = new TransformStream(new PerformanceMonitoringTransform());
447
448
vueStream.pipeTo(writable);
449
return new Response(readable);
450
```
451
452
## Environment Considerations
453
454
### Browser Compatibility
455
456
Web Streams are supported in:
457
- Modern browsers (Chrome 78+, Firefox 102+, Safari 14.1+)
458
- Edge computing platforms (CloudFlare Workers, Vercel Edge, Deno Deploy)
459
- Node.js 18+ (with `--experimental-web-streams` flag)
460
461
### Memory Efficiency
462
463
Web Streams provide excellent memory characteristics:
464
- Backpressure handling prevents memory bloat
465
- Automatic garbage collection of processed chunks
466
- Suitable for large responses and constrained environments
467
468
### Error Recovery
469
470
Web Streams include robust error handling:
471
- Stream-level error propagation
472
- Graceful degradation options
473
- Timeout and cancellation support