Server-side rendering library for Vue.js applications with support for streaming and multiple environments
—
Native Node.js stream integration for traditional server environments and frameworks. These functions provide seamless integration with Node.js's built-in stream system, making it easy to integrate Vue SSR into existing Node.js applications and frameworks.
Creates a Node.js Readable stream that outputs the rendered HTML. This function is only available in the CommonJS build and provides direct integration with Node.js stream ecosystem.
/**
* Renders input as a Node.js Readable stream
* @param input - Vue application instance or VNode to render
* @param context - Optional SSR context for teleports and additional data
* @returns Node.js Readable stream containing the rendered HTML
* @throws Error if used in ESM build - use pipeToNodeWritable instead
*/
function renderToNodeStream(
input: App | VNode,
context?: SSRContext
): Readable;Usage Examples:
import { createSSRApp } from "vue";
import { renderToNodeStream } from "@vue/server-renderer";
import { pipeline } from "stream";
const app = createSSRApp({
template: `
<div>
<h1>Node.js Streaming</h1>
<p>Content streamed via Node.js Readable</p>
</div>
`,
});
// Direct streaming to HTTP response
import express from "express";
const server = express();
server.get("/", (req, res) => {
res.setHeader('Content-Type', 'text/html');
const stream = renderToNodeStream(app);
stream.pipe(res);
// Handle errors
stream.on('error', (err) => {
console.error('Streaming error:', err);
res.status(500).end('Internal Server Error');
});
});
// Pipeline with transforms
import { Transform } from "stream";
const htmlWrapper = new Transform({
objectMode: false,
transform(chunk, encoding, callback) {
// Wrap content in HTML document
if (!this.headerSent) {
this.push('<!DOCTYPE html><html><head><title>My App</title></head><body>');
this.headerSent = true;
}
this.push(chunk);
callback();
},
flush(callback) {
this.push('</body></html>');
callback();
}
});
pipeline(
renderToNodeStream(app),
htmlWrapper,
process.stdout,
(err) => {
if (err) console.error('Pipeline error:', err);
else console.log('Pipeline complete');
}
);Legacy function that creates a Node.js Readable stream. This function is deprecated and should be replaced with renderToNodeStream.
/**
* @deprecated Use renderToNodeStream instead
* Renders input as a Node.js Readable stream
* @param input - Vue application instance or VNode to render
* @param context - Optional SSR context for teleports and additional data
* @returns Node.js Readable stream containing the rendered HTML
*/
function renderToStream(
input: App | VNode,
context?: SSRContext
): Readable;Migration:
// Old (deprecated)
const stream = renderToStream(app, context);
// New (recommended)
const stream = renderToNodeStream(app, context);Pipes the rendered output directly to an existing Node.js Writable stream. This function works in both CommonJS and ESM builds and provides more control over the destination.
/**
* Render and pipe to an existing Node.js Writable stream instance
* @param input - Vue application instance or VNode to render
* @param context - Optional SSR context for teleports and additional data
* @param writable - Node.js Writable stream to pipe the output to
*/
function pipeToNodeWritable(
input: App | VNode,
context?: SSRContext,
writable: Writable
): void;Usage Examples:
import { createSSRApp } from "vue";
import { pipeToNodeWritable } from "@vue/server-renderer";
import { createWriteStream } from "fs";
import { Transform } from "stream";
const app = createSSRApp({
template: `
<div>
<h1>Piped Content</h1>
<p>This is piped to a writable stream</p>
</div>
`,
});
// Pipe to file
const fileStream = createWriteStream('output.html');
pipeToNodeWritable(app, {}, fileStream);
// Pipe to HTTP response
import express from "express";
const server = express();
server.get("/", (req, res) => {
res.setHeader('Content-Type', 'text/html');
pipeToNodeWritable(app, {}, res);
});
// Pipe through transform streams
const compressionStream = new Transform({
transform(chunk, encoding, callback) {
// Add compression or other transformations
const compressed = this.compress(chunk);
callback(null, compressed);
}
});
pipeToNodeWritable(app, {}, compressionStream);
compressionStream.pipe(process.stdout);Both functions support SSR context for handling teleports and passing data:
import { createSSRApp } from "vue";
import { pipeToNodeWritable } from "@vue/server-renderer";
const app = createSSRApp({
template: `
<div>
<h1>Main Content</h1>
<Teleport to="#sidebar">
<div>Sidebar content</div>
</Teleport>
</div>
`,
});
const context = {
userAgent: req.headers['user-agent'],
requestId: generateRequestId(),
};
// Custom writable that handles teleports
class TeleportAwareWritable extends Writable {
constructor(private response: Response) {
super();
this.chunks = [];
}
_write(chunk, encoding, callback) {
this.chunks.push(chunk);
callback();
}
_final(callback) {
// Combine main content with teleports
const mainContent = Buffer.concat(this.chunks).toString();
let fullHtml = '<!DOCTYPE html><html><body>';
fullHtml += mainContent;
// Add teleported content
if (context.teleports) {
for (const [target, content] of Object.entries(context.teleports)) {
fullHtml += `<div id="${target.slice(1)}">${content}</div>`;
}
}
fullHtml += '</body></html>';
this.response.end(fullHtml);
callback();
}
}
const writable = new TeleportAwareWritable(res);
pipeToNodeWritable(app, context, writable);import express from "express";
import { createSSRApp } from "vue";
import { renderToNodeStream, pipeToNodeWritable } from "@vue/server-renderer";
const app = express();
// Using renderToNodeStream
app.get("/stream", (req, res) => {
const vueApp = createSSRApp({
template: `<div>Hello from Express + Vue SSR!</div>`
});
res.setHeader('Content-Type', 'text/html');
const stream = renderToNodeStream(vueApp);
stream.on('error', (err) => {
console.error('SSR Error:', err);
res.status(500).end('Server Error');
});
stream.pipe(res);
});
// Using pipeToNodeWritable (preferred for ESM)
app.get("/pipe", (req, res) => {
const vueApp = createSSRApp({
template: `<div>Hello from Express + Vue SSR (piped)!</div>`
});
res.setHeader('Content-Type', 'text/html');
pipeToNodeWritable(vueApp, {}, res);
});import Fastify from "fastify";
import { createSSRApp } from "vue";
import { pipeToNodeWritable } from "@vue/server-renderer";
const fastify = Fastify({ logger: true });
fastify.get("/", async (request, reply) => {
const vueApp = createSSRApp({
template: `<div>Hello from Fastify + Vue SSR!</div>`
});
reply.type('text/html');
pipeToNodeWritable(vueApp, {}, reply.raw);
});import Koa from "koa";
import { createSSRApp } from "vue";
import { pipeToNodeWritable } from "@vue/server-renderer";
const app = new Koa();
app.use(async (ctx) => {
const vueApp = createSSRApp({
template: `<div>Hello from Koa + Vue SSR!</div>`
});
ctx.type = 'text/html';
pipeToNodeWritable(vueApp, {}, ctx.res);
});import { pipeToNodeWritable } from "@vue/server-renderer";
import { Writable } from "stream";
class ErrorHandlingWritable extends Writable {
constructor(private response: Response) {
super();
}
_write(chunk, encoding, callback) {
try {
this.response.write(chunk);
callback();
} catch (err) {
callback(err);
}
}
_final(callback) {
this.response.end();
callback();
}
}
const writable = new ErrorHandlingWritable(res);
writable.on('error', (err) => {
console.error('Streaming error:', err);
if (!res.headersSent) {
res.status(500).end('Internal Server Error');
}
});
pipeToNodeWritable(app, {}, writable);import { performance } from "perf_hooks";
import { pipeToNodeWritable } from "@vue/server-renderer";
const startTime = performance.now();
let firstChunkTime: number;
let chunkCount = 0;
const monitoringWritable = new Writable({
write(chunk, encoding, callback) {
if (chunkCount === 0) {
firstChunkTime = performance.now();
console.log(`Time to first chunk: ${firstChunkTime - startTime}ms`);
}
chunkCount++;
this.push(chunk);
callback();
},
final(callback) {
const endTime = performance.now();
console.log(`Total render time: ${endTime - startTime}ms`);
console.log(`Total chunks: ${chunkCount}`);
callback();
}
});
pipeToNodeWritable(app, {}, monitoringWritable);pipeToNodeWritable for maximum compatibilityNode.js streaming provides excellent memory efficiency:
Node.js streams provide robust error handling mechanisms:
Install with Tessl CLI
npx tessl i tessl/npm-vue--server-renderer