@andore/electron-start is a Vite-powered build-time transformer that eliminates the manual boilerplate of Electron's Inter-Process Communication (IPC). Heavily inspired by the "Server Functions" pattern in TanStack Start, it allows you to define type-safe, validated functions that bridge the gap between your Main and Renderer processes effortlessly.
In traditional Electron development, IPC is a fragmented mess. @andore/electron-start collapses these layers into a single, cohesive definition.
- 🔒 End-to-End Type Safety: Shared types between processes.
- ✅ Built-in Validation: First-class Zod support for input validation.
- 🪄 Zero Boilerplate: No more manual
contextBridgeor magic strings. - 🏗️ Tree-Shakable: Code is surgically extracted to the correct process bundle.
- ⚡ Developer Velocity: Write logic once, call it like a regular function.
- 🛠️ Powered by vite-plugin-electron
Define your IPC logic in a file ending with .ipc.ts. @andore/electron-start handles the rest.
// src/shared/file-ops.ipc.ts
import { createIpcFn } from '@andore/electron-start';
import { z } from 'zod';
export const readFile = createIpcFn()
.inputValidator(z.string()) // Automatic validation!
.handler(async (filePath, context) => {
// This body ONLY runs in the Main process.
// Node.js APIs are safe to use here.
const fs = await import('node:fs/promises');
return await fs.readFile(filePath, 'utf-8');
});Just import and call. No window.api nonsense.
// App.tsx
import { readFile } from './shared/file-ops.ipc';
function App() {
const handleOpen = async () => {
// Fully typed! Result is string.
const content = await readFile('/path/to/config.json');
console.log(content);
};
}At build time, the Vite plugin splits this function:
- Main Process: Becomes an
ipcMain.handlelistener. - Preload: Injected into the
contextBridge. - Renderer: Becomes a lightweight proxy calling
ipcRenderer.invoke.
pnpm add @andore/electron-startAdd electronStart to your main Vite config. It automatically configures vite-plugin-electron for you, managing the complex build orchestrations for Main, Preload, and Renderer processes seamlessly.
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { electronStart } from '@andore/electron-start/plugin/vite';
export default defineConfig({
plugins: [
react(),
electronStart({
mainEntry: 'electron/main-process.ts',
preloadEntry: 'electron/preload.ts'
})
]
});Inject state or services into your IPC handlers in the Main process.
// main.ts
import { setIpcContext } from '@andore/electron-start';
setIpcContext({
db: new Database(),
mainWindow: myWindow
});This project is a love letter to TanStack Start. We believe that the boundary between processes (or client/server) should be a type-safe implementation detail, not a chore for the developer.
@andore/electron-start follows Electron security best practices by default:
- Uses
contextBridgefor secure exposure. - Encourages
inputValidator(Zod) to sanitize IPC inputs. - Keeps Node.js logic out of the Renderer bundle via build-time extraction.