vuejs/core · error · Error

ESM build of renderToStream() does not support renderToNodeS

Error message

ESM build of renderToStream() does not support renderToNodeStream(). Use pipeToNodeWritable() with an existing Node.js Writable stream instance instead.

What it means

Thrown by renderToNodeStream in @vue/server-renderer's ESM build. renderToNodeStream constructs a Node `stream.Readable` via require('node:stream'), which only works in the CJS build; in the ESM build the require returns null and the guard at renderToStream.ts:116 throws. The message points to pipeToNodeWritable() as the ESM-compatible alternative.

Source

Thrown at packages/server-renderer/src/renderToStream.ts:116

  input: App | VNode,
  context: SSRContext = {},
): Readable {
  console.warn(
    `[@vue/server-renderer] renderToStream is deprecated - use renderToNodeStream instead.`,
  )
  return renderToNodeStream(input, context)
}

export function renderToNodeStream(
  input: App | VNode,
  context: SSRContext = {},
): Readable {
  const stream: Readable = __CJS__
    ? new (require('node:stream').Readable)({ read() {} })
    : null

  if (!stream) {
    throw new Error(
      `ESM build of renderToStream() does not support renderToNodeStream(). ` +
        `Use pipeToNodeWritable() with an existing Node.js Writable stream ` +
        `instance instead.`,
    )
  }

  return renderToSimpleStream(input, context, stream)
}

export function pipeToNodeWritable(
  input: App | VNode,
  context: SSRContext | undefined = {},
  writable: Writable,
): void {
  renderToSimpleStream(input, context, {
    push(content) {
      if (content != null) {
        writable.write(content)

View on GitHub (pinned to a2b40db9a8)

Solutions

  1. Use pipeToNodeWritable(input, context, existingWritable) instead — it accepts an already-created Node Writable stream and works in the ESM build.
  2. If you must keep renderToNodeStream, import the CJS build of @vue/server-renderer explicitly.
  3. Switch the server code to create the Writable (e.g. the HTTP response) and pipe into it via pipeToNodeWritable.

Example fix

// before (ESM build)
const stream = renderToNodeStream(app)
stream.pipe(res)

// after
pipeToNodeWritable(app, {}, res)
Defensive patterns

Strategy: validation

Validate before calling

// Detect the ESM build and route to the writable API.
import { pipeToNodeWritable, renderToNodeStream } from '@vue/server-renderer'
async function ssrToResponse(app, res) {
  // Prefer the universal pipe API; fall back only on the CJS build.
  if (typeof pipeToNodeWritable === 'function') {
    pipeToNodeWritable(app, {}, res)
  } else {
    renderToNodeStream(app).pipe(res)
  }
}

Type guard

function supportsRenderToNodeStream(): boolean {
  // True only on the CJS build of @vue/server-renderer.
  return typeof __CJS__ !== 'undefined' ? !!__CJS__ : false
}

Prevention

When it happens

Trigger: Calling renderToNodeStream(input) from the ESM entry of @vue/server-renderer. This happens in pure-ESM Node projects or bundlers that pick the ESM build.

Common situations: Migrating an SSR server to native ESM (`"type": "module"`); a bundler resolving @vue/server-renderer to its ESM entry; copying renderToNodeStream usage from a CJS tutorial into an ESM project.

Related errors


AI-assisted analysis of vuejs/core@a2b40db9a8 (2026-08-12). Data as JSON: /api/errors/732dcb37551c4a60. Report an issue: GitHub.