or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdinternal-helpers.mdnodejs-integration.mdstreaming-rendering.mdstring-rendering.mdweb-streams.md

web-streams.mddocs/

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