Modern Vite plugin for Electron Renderer process - enables using Node.js APIs and native modules.
- 🚀 Vite 5, 6 & 7 Support - Works with all modern Vite versions
- ⚡ Electron 32+ Support - Updated for modern Electron
- 📦 Node.js Built-ins - Use
fs,path,crypto, etc. in renderer - 🔌 Native Modules - Support for C/C++ native addons like
serialport,sqlite3 - 📄 ESM Packages - Pre-bundle ESM-only packages for Electron
- 🛠️ TypeScript - Written in TypeScript with full type definitions
- 🔧 Zero Config - Works out of the box for most use cases
npm install plugin-vite-electron-renderer -D
# or
pnpm add plugin-vite-electron-renderer -D
# or
yarn add plugin-vite-electron-renderer -DJust add the plugin to your Vite config - no configuration needed for basic Node.js API usage:
// vite.config.ts
import { defineConfig } from 'vite'
import renderer from 'plugin-vite-electron-renderer'
export default defineConfig({
plugins: [renderer()],
})Now you can use Node.js APIs in your renderer process:
// In your Electron renderer code
import { ipcRenderer } from 'electron'
import fs from 'node:fs'
import path from 'node:path'
const content = fs.readFileSync(path.join(__dirname, 'data.json'), 'utf-8')This plugin works great with vite-plugin-electron:
// vite.config.ts
import { defineConfig } from 'vite'
import electron from 'vite-plugin-electron'
import renderer from 'plugin-vite-electron-renderer'
export default defineConfig({
plugins: [
electron({
entry: 'electron/main.ts',
}),
renderer(),
],
})For native modules like serialport or sqlite3, configure them as cjs:
// vite.config.ts
import { defineConfig } from 'vite'
import renderer from 'plugin-vite-electron-renderer'
export default defineConfig({
plugins: [
renderer({
resolve: {
serialport: { type: 'cjs' },
sqlite3: { type: 'cjs' },
'better-sqlite3': { type: 'cjs' },
},
}),
],
})// In your Electron renderer code
import SerialPort from 'serialport'
const port = new SerialPort({ path: '/dev/tty-usbserial1', baudRate: 9600 })For pure ESM packages that need pre-bundling:
// vite.config.ts
import { defineConfig } from 'vite'
import renderer from 'plugin-vite-electron-renderer'
export default defineConfig({
plugins: [
renderer({
resolve: {
'node-fetch': { type: 'esm' },
got: { type: 'esm' },
execa: { type: 'esm' },
},
}),
],
})Returns a Vite plugin that enables Node.js API usage in Electron's renderer process.
interface ElectronRendererOptions {
resolve?: Record<string, ModuleResolveConfig>
}| Option | Type | Default | Description |
|---|---|---|---|
resolve |
Record<string, ModuleResolveConfig> |
{} |
Configuration for third-party modules |
interface ModuleResolveConfig {
type: 'cjs' | 'esm'
build?: (args: CustomBuildArgs) => Promise<string>
}| Property | Type | Required | Description |
|---|---|---|---|
type |
'cjs' | 'esm' |
Yes | How to load the module |
build |
Function |
No | Custom build function for advanced use cases |
-
cjs: Loads the module usingrequire(). Best for:- C/C++ native addons (
serialport,better-sqlite3, etc.) - CommonJS packages
- Packages that are already bundled
- C/C++ native addons (
-
esm: Pre-bundles the module to CJS format using esbuild. Best for:- Pure ESM packages (
node-fetch,got,execa, etc.) - Packages with ESM-only dependencies
- Packages that need bundling for Electron compatibility
- Pure ESM packages (
For full control over how a module is pre-bundled:
renderer({
resolve: {
'my-special-module': {
type: 'cjs',
build: async ({ cjs, esm }) => {
// Use esm() to pre-bundle with custom esbuild options
return await esm('my-special-module', {
external: ['some-peer-dependency'],
define: { 'process.env.NODE_ENV': '"production"' },
})
},
},
},
})The build function receives:
cjs(module): Generate a require() wrapper for a CJS moduleesm(module, buildOptions?): Pre-bundle an ESM module with optional esbuild options
┌────────────────────────────────────────┐ ┌─────────────────┐
│ import { ipcRenderer } from 'electron' │ │ Vite Dev Server │
└────────────────────────────────────────┘ └─────────────────┘
│ │
│ 1. Request 'electron' module │
│ ───────────────────────────────────>│
│ │
│ 2. Alias redirects to cache: │
│ node_modules/.vite-electron- │
│ renderer/electron.mjs │
│ │
│ 3. Cache file contains: │
│ const e = require('electron') │
│ export const ipcRenderer = ... │
│ │
│ 4. Response with ESM module │
│ <───────────────────────────────────│
▼ ▼
The plugin creates ESM wrapper modules that:
- Intercept imports - Catches imports of
electronand Node.js builtins - Generate wrappers - Creates
.mjsfiles innode_modules/.vite-electron-renderer/ - Use require() - The wrappers use
require()internally (works withnodeIntegration) - Export as ESM - Re-exports everything as ESM for Vite compatibility
- Handle edge cases - Special handling for Web Workers where
ipcRendererisn't available
The generated wrappers use this pattern:
const avoid_parse_require = require;
const _M_ = avoid_parse_require("fs");This prevents bundlers from statically analyzing and transforming the require() call, ensuring it remains intact for runtime execution in Electron.
- Node.js: >= 20.19.0 or >= 22.12.0 (required by Vite 7)
- Vite: ^5.0.0 || ^6.0.0 || ^7.0.0
- Electron: >= 32.0.0 (with
nodeIntegration: true)
Note: Node.js 18 reached EOL in April 2025. Vite 7 requires Node.js 20.19+ or 22.12+.
For this plugin to work, you need nodeIntegration enabled in your BrowserWindow:
// main.ts (Electron main process)
import { app, BrowserWindow } from 'electron'
app.whenReady().then(() => {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
})
// Load your Vite dev server or built files
if (process.env.VITE_DEV_SERVER_URL) {
win.loadURL(process.env.VITE_DEV_SERVER_URL)
} else {
win.loadFile('dist/index.html')
}
})
⚠️ Security Note: EnablingnodeIntegrationand disablingcontextIsolationreduces security. Only do this if you trust all content loaded in the renderer. For production apps with untrusted content, consider using a preload script withcontextIsolation: trueinstead.
| Module Type | dependencies | devDependencies |
|---|---|---|
| C/C++ native modules | ✅ Required | ❌ |
| CJS packages | ✅ | ✅ |
| ESM packages | ✅ | ✅ (Recommended) |
| Web packages (React, Vue) | ✅ | ✅ (Recommended) |
Putting buildable packages in devDependencies reduces the final app size when using electron-builder.
This usually means an ESM package isn't configured. Add it to resolve:
renderer({
resolve: {
'problematic-package': { type: 'esm' },
},
})- Ensure it's in
dependencies(notdevDependencies) - Configure it as
cjs:
renderer({
resolve: {
'native-module': { type: 'cjs' },
},
})- Rebuild native modules for Electron:
npx electron-rebuildMake sure nodeIntegration: true is set in your BrowserWindow's webPreferences.
ipcRenderer doesn't work in Web Workers - this is an Electron limitation. The plugin provides helpful error messages when this is attempted.
Workaround: Use postMessage to communicate between the Worker and renderer, then use ipcRenderer in the renderer thread.
// worker.ts
self.postMessage({ type: 'send-to-main', data: 'hello' })
// renderer.ts
worker.onmessage = (e) => {
if (e.data.type === 'send-to-main') {
ipcRenderer.send('channel', e.data.data)
}
}If a module's exports appear as undefined, it might have a non-standard export pattern. Try using a custom build function:
renderer({
resolve: {
'weird-module': {
type: 'cjs',
build: async ({ cjs }) => {
// Custom wrapper generation
return `
const m = require('weird-module');
export default m;
export const specificExport = m.specificExport;
`
},
},
},
})# Clone and install
git clone https://github.com/dmazzella/plugin-vite-electron-renderer.git
cd plugin-vite-electron-renderer
npm install
# Build the plugin
npm run build
# Run tests
npm run test
# Development mode (watch)
npm run dev# First build the plugin
npm run build
# Then run an example
cd examples/basic
npm install
npm run devplugin-vite-electron-renderer/
├── src/
│ ├── index.ts # Main plugin entry point
│ ├── constants.ts # Plugin constants and Electron API definitions
│ ├── snippets.ts # Code generation for module wrappers
│ └── types.ts # TypeScript type definitions
├── test/
│ ├── config.test.ts
│ ├── constants.test.ts
│ └── snippets.test.ts
├── examples/
│ └── basic/ # Basic usage example
└── dist/ # Built output
MIT
Inspired by vite-plugin-electron-renderer by Leo Wang (草鞋没号).