Electron
ZNTC ships no Electron-specific integration. Like webpack / rspack, you bundle main · preload · renderer as three separate entries with ZNTC and wire them together via npm scripts. The renderer is served by ZNTC’s own dev server, so no extra plugin is needed.
Project layout
Section titled “Project layout”my-electron-app/├── src/│ ├── main.ts # Electron main process│ ├── preload.ts # contextBridge preload│ └── renderer/│ ├── index.html│ └── app.tsx├── dist/ # build output└── package.jsonmain · preload (Node · CJS)
Section titled “main · preload (Node · CJS)”Keep electron and Node built-ins external; emit as CommonJS.
{ "scripts": { "build:main": "zntc --bundle src/main.ts --platform=node --format=cjs --packages=external -o dist/main.cjs", "build:preload": "zntc --bundle src/preload.ts --platform=node --format=cjs --packages=external -o dist/preload.cjs" }}--packages=external: keeps every bare import (electron, Node built-ins,electron-store, …) external; Electronrequires them at runtime.--format=cjs: CJS is the most compatible format for the Electron main process. (Use--format=esmif you target Electron 28+ ESM main.)
renderer (browser · dev server)
Section titled “renderer (browser · dev server)”The renderer is a plain SPA. Serve it with ZNTC’s dev server and have the main process point BrowserWindow at it.
{ "scripts": { "dev:renderer": "zntc dev src/renderer", "build:renderer": "zntc build src/renderer" }}import { app, BrowserWindow } from "electron";import { join } from "node:path";
const isDev = process.env.NODE_ENV === "development";
app.whenReady().then(() => { const win = new BrowserWindow({ webPreferences: { preload: join(__dirname, "preload.cjs") }, });
if (isDev) { win.loadURL("http://localhost:12300"); } else { win.loadFile(join(__dirname, "renderer/index.html")); }});Dev / prod scripts
Section titled “Dev / prod scripts”Watch · restart · concurrent execution is wired with standard tooling (npm-run-all, wait-on, nodemon) — the same pattern as webpack/rspack’s Electron guides.
{ "scripts": { "dev:renderer": "zntc dev src/renderer", "dev:main": "zntc --bundle src/main.ts --platform=node --format=cjs --packages=external --watch -o dist/main.cjs", "dev:preload": "zntc --bundle src/preload.ts --platform=node --format=cjs --packages=external --watch -o dist/preload.cjs", "dev:electron": "wait-on tcp:12300 && nodemon --watch dist/main.cjs --watch dist/preload.cjs --exec electron dist/main.cjs", "dev": "npm-run-all -p dev:renderer dev:main dev:preload dev:electron",
"build:main": "zntc --bundle src/main.ts --platform=node --format=cjs --packages=external -o dist/main.cjs", "build:preload": "zntc --bundle src/preload.ts --platform=node --format=cjs --packages=external -o dist/preload.cjs", "build:renderer": "zntc build src/renderer", "build": "npm-run-all build:main build:preload build:renderer",
"package": "electron-builder" }}Renderer HMR is handled by ZNTC’s dev server (see Dev Server). When the main / preload bundles rebuild, nodemon restarts the Electron process.
Packaging
Section titled “Packaging”Use electron-builder or electron-forge’s generic mode as-is — ZNTC’s output is plain Node CJS / browser bundles, with no extra transform step required.
// package.json — electron-builder example{ "build": { "appId": "com.example.app", "files": ["dist/**/*", "package.json"] }}- ZNTC itself does not auto-restart Electron when the main process rebuilds — the
nodemonscript above plays that role. - IPC type sync,
autoUpdater, and other Electron ecosystem tools work independently of ZNTC.