vite-plugin-virtual-html

raw JSON →
1.2.7 verified Mon Apr 27 auth: no javascript

Vite plugin for flexible multi-page app (MPA) development, allowing HTML files to be placed anywhere in the project and configured via a pages map. Current stable version is 1.2.7, released periodically since 2020. Key differentiators: eliminates Vite's default HTML root restriction, provides dev-server interception for seamless MPA development, supports EJS templating with custom render functions, and includes automatic Rollup input configuration. Compared to vanilla Vite MPA, it offers a configurable pages structure similar to @vue/cli's pages option, with additional features like injectCode and urlTransformer.

error TypeError: virtualHtml is not a function
cause Using CJS require() with ESM-only version >=1.2.0
fix
Switch to ESM import or use dynamic import()
error [vite]: Rollup failed to resolve import "..." from "..."
cause HTML file not found during build; often caused by missing file or incorrect paths in pages config
fix
Verify all pages paths are correct and files exist; also ensure extraGlobPattern excludes dist
error Error: ENOENT: no such file or directory, open '/path/to/project/root/index.html'
cause Dev mode: requested HTML file is not in pages config or path is wrong
fix
Add the HTML file path to the pages config or ensure the requested URL matches a configured page
error Plugin page config: index is not a valid page config
cause pages entry must be a string (path) or an object with 'template' key; using other type
fix
Ensure each page value is either a string path or an object: { template: 'path', data: {...} }
breaking Dropped support for CJS in v1.2.0; require() will fail
fix Use ESM imports (import virtualHtml from 'vite-plugin-virtual-html') or downgrade to v1.1.x
gotcha html files must exist on disk in dev mode? No, but if missing, dev server returns 404 (since v1.1.20)
fix Ensure all configured HTML files exist or handle 404 gracefully
gotcha build.rollupOptions.input is overridden by plugin; manual input config may conflict
fix Remove manual input config; plugin auto-generates it from pages
deprecated option 'page' was renamed to 'template' in v0.2.0
fix Use 'template' key for HTML file path in page objects
gotcha injectCode only works when specifying html file pattern with '*' (globbing)
fix Use '*' as key in injectCode to apply to all HTML files, or specify individual file paths
breaking viteConfig.appType is set to 'custom' by default since v1.2.4 (useCustom: true), which disables Vite's SPA fallback
fix Set useCustom: false to revert to pre-1.2.4 behavior (appType auto-detected)
gotcha Build copies HTML files to project root before building; if process crashes, root may get polluted
fix Clean up root HTML files manually if build is interrupted; known limitation
npm install vite-plugin-virtual-html
yarn add vite-plugin-virtual-html
pnpm add vite-plugin-virtual-html

Basic setup for a multi-page app with virtual-html plugin, showing pages config, indexPage fallback, and global data.

// vite.config.ts
import { defineConfig } from 'vite';
import virtualHtml from 'vite-plugin-virtual-html';

export default defineConfig({
  plugins: [
    virtualHtml({
      pages: {
        index: '/src/pages/index/index.html',
        about: '/src/pages/about/about.html',
      },
      indexPage: 'index',
      data: { title: 'My App' },
    }),
  ],
});

// Create HTML files somewhere in project
// /src/pages/index/index.html
// /src/pages/about/about.html

// Run `npm run dev` – plugin serves HTML from configured paths.
// Run `npm run build` – plugin configures Rollup input and copies files.