Vue Bundle Renderer
Vue Bundle Renderer provides robust server-side rendering (SSR) capabilities for Vue 3 applications, serving as a modern and optimized alternative to the legacy `vue-server-renderer` package. It is deeply integrated into the Nuxt.js ecosystem and is designed to work efficiently with modern JavaScript bundlers like Vite and Webpack. The current stable version is 2.2.0, with a relatively active release cadence, frequently publishing enhancements and fixes, as seen in recent updates for Vite manifest compatibility. Its primary function is to take a Vue application entry and a manifest file (from Vite or Webpack) to generate an HTML string on the server, ensuring proper asset injection and hydration markers for client-side Vue applications. Key differentiators include its focus on Vue 3's composition API, robust TypeScript support, and adaptability to contemporary build tools, offering a more streamlined SSR workflow compared to its predecessor.
Common errors
-
Error: Cannot find module 'vue-bundle-renderer/runtime' or 'vue-bundle-renderer'
cause This error typically occurs if you are trying to use `vue-bundle-renderer` in a CommonJS environment without proper transpilation, or if you are using an incorrect subpath import for `createRenderer`.fixEnsure your Node.js project is configured for ESM imports (e.g., `"type": "module"` in `package.json` or run with a transpiler). For `createRenderer`, always use `import { createRenderer } from 'vue-bundle-renderer/runtime'`. -
TypeError: Cannot read properties of undefined (reading 'legacyEntry') or similar errors related to manifest parsing.
cause The Vite or Webpack manifest object provided to the renderer has an incompatible structure for the `vue-bundle-renderer` version being used, or the manifest file itself is corrupted/incorrectly generated.fixVerify that your bundler (Vite or Webpack) is generating the manifest correctly. Ensure your `vue-bundle-renderer` version is compatible with your bundler's manifest format (e.g., `vue-bundle-renderer@2.1.2+` for Vite v7 manifests). Utilize `normalizeViteManifest` or `normalizeWebpackManifest` if your manifest needs pre-processing before passing it to the renderer. -
Hydration mismatch warning or unexpected client-side rendering behavior in the browser console.
cause The HTML generated on the server by `vue-bundle-renderer` does not precisely match the HTML subsequently rendered by Vue on the client-side. Common causes include client-side only code running during SSR, direct DOM manipulation before hydration, or differences in rendering logic between environments.fixEnsure your Vue application is fully isomorphic. Avoid client-specific code paths during SSR; use lifecycle hooks like `onMounted` for client-side only operations. Debug by comparing the server-rendered HTML output with the initial client-rendered HTML before hydration takes over.
Warnings
- breaking Migrating from `vue-server-renderer` (the Vue 2 era SSR package) to `vue-bundle-renderer` for Vue 3 is a significant architectural change. The API, manifest handling, and integration patterns are fundamentally different. Direct migration is not possible; expect a complete rewrite of your SSR logic.
- gotcha The `createRenderer` function must be imported from the `/runtime` subpath (`vue-bundle-renderer/runtime`). Attempting to import it directly from the root package (`vue-bundle-renderer`) will result in a runtime error indicating the symbol is undefined or cannot be found.
- breaking Version 2.1.2 introduced specific support for Vite v7 manifest types, denoted by `Fixes: deps: Support vite v7 manifest type`. While this improved compatibility, it implies that older versions of `vue-bundle-renderer` (pre-2.1.2) may not correctly parse or utilize Vite v7 generated manifests, leading to rendering errors or missing assets.
- gotcha During the 2.1.x release cycle, there was a temporary regression (fixed in v2.2.0) where an export might have been missing or incorrectly handled (`Fixes: Add back export`). Users who installed specific patches within this range might have encountered import errors for certain symbols.
Install
-
npm install vue-bundle-renderer -
yarn add vue-bundle-renderer -
pnpm add vue-bundle-renderer
Imports
- createRenderer
import { createRenderer } from 'vue-bundle-renderer'import { createRenderer } from 'vue-bundle-renderer/runtime' - normalizeViteManifest
const { normalizeViteManifest } = require('vue-bundle-renderer')import { normalizeViteManifest } from 'vue-bundle-renderer' - normalizeWebpackManifest
import { normalizeWebpackManifest } from 'vue-bundle-renderer/runtime'import { normalizeWebpackManifest } from 'vue-bundle-renderer'
Quickstart
import { createSSRApp } from 'vue';
import { createRenderer } from 'vue-bundle-renderer/runtime';
// 1. Define your Vue 3 SSR-friendly application
const createVueApp = () => createSSRApp({
data: () => ({ message: 'Hello Vue SSR!' }),
template: `
<div>
<h1>{{ message }}</h1>
<p>This content was rendered on the server.</p>
<button @click="console.log('Hydrated click!')">Click Me (Hydrates!)</button>
</div>
`,
});
async function renderApplication() {
// 2. Initialize the renderer. In a production app, `renderOptions`
// would typically include a bundle and manifest generated by Vite or Webpack.
// For this basic example, we'll omit them to focus on core rendering.
const renderer = createRenderer(createVueApp, {
// bundle: { /* ... webpack or vite server bundle */ },
// manifest: { /* ... webpack or vite client manifest */ }
});
// 3. Define an SSR context. This object can be used by the Vue app
// to inject data (e.g., head tags, styles) into the HTML document.
const ssrContext: { head?: string; scripts?: string } = {};
// 4. Render the Vue application to an HTML string.
const appHtml = await renderer.renderToString(ssrContext);
// 5. Construct a complete HTML page with the rendered content.
const fullHtml = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Bundle Renderer Example</title>
${ssrContext.head || ''} <!-- Injected head tags -->
</head>
<body>
<div id="app">${appHtml}</div>
${ssrContext.scripts || ''} <!-- Injected scripts for hydration -->
<script>
// Basic client-side hydration for this example (in a real app, bundler handles this)
import { createApp } from 'vue';
const app = createApp({
data: () => ({ message: 'Hello Vue SSR!' }),
template: document.getElementById('app').innerHTML // Or load from bundle
});
// app.mount('#app'); // This would cause re-render, use hydrate with client bundle
</script>
</body>
</html>
`;
console.log(fullHtml);
}
renderApplication().catch(console.error);