| name | electrobun-desktop-apps |
| description | Build desktop apps with Electrobun โ TypeScript-first framework using Bun + native webviews |
Electrobun Desktop Apps
Build lightweight, fast desktop applications using Electrobun โ a TypeScript-first framework with Bun runtime and native system webviews.
When To Use
- Building desktop apps with TypeScript
- Need lightweight alternative to Electron (14MB vs 150MB+)
- Want native webviews (WebKit/WebView2/WebKitGTK) instead of bundled Chromium
- Need typed IPC between main process and browser windows
- Building cross-platform apps (macOS, Windows, Linux)
Quick Start
bunx electrobun init
bunx electrobun init react-tailwind-vite
cd my-app && bun install && bun start
Architecture
- Zig launcher โ starts Bun runtime โ spawns Web Worker (your code)
- Main thread: native GUI event loop via Bun FFI
- Your TypeScript: runs in web worker, calls Electrobun APIs
- Views: system native webviews (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux)
- Optional CEF (Chromium) bundling for consistency (adds ~86MB)
Project Structure
my-app/
src/
bun/
index.ts # Main process entry โ BrowserWindow, menus, tray
shared/
types.ts # Typed RPC schemas (shared between bun + views)
mainview/
index.html # UI template
index.css # Styles
index.ts # Frontend logic (transpiled to views://mainview/)
electrobun.config.ts # Build configuration
package.json
tsconfig.json
Key Imports
import { BrowserWindow, BrowserView, ApplicationMenu, ContextMenu, Tray, Updater, Utils, PATHS } from "electrobun/bun";
import Electrobun from "electrobun/bun";
import { Electroview } from "electrobun/view";
Critical Rules
- Remove
"type": "module" and "module" from package.json (bun init adds these โ they break Electrobun)
- If you remove
"type": "module" in a Node-tooled repo, use eslint.config.mjs for flat config (or Node will warn/reparse config on every run)
- Never write to
PATHS.RESOURCES_FOLDER at runtime โ breaks code signing
- Builds are host-platform only โ use CI runners for cross-platform
- Linux: strongly recommend
bundleCEF: true (WebKitGTK has severe OOPIF limitations)
- Set
exitOnLastWindowClosed: true in runtime config unless you want tray-only behavior
- In Electron migrations, Bun/Electrobun builds may not honor TS path aliases automatically (
@main/*, @shared/*) โ add a Bun build plugin in electrobun.config.ts to resolve aliases
- If
electrobun build only reports Bundle failed, run electrobun dev --watch to reveal concrete bundling errors
- Prefer direct scripts (
electrobun dev --console, electrobun build --env=...) over custom postbuild wrappers unless wrappers are proven necessary
- Electrobun packaged release assets are emitted in
artifacts/ (while app bundle internals live under build/) โ CI release upload steps should target artifacts/**
- Release upload filters should include Electrobun update artifacts (for example
*.tar.zst and *-update.json) instead of only legacy latest*.json patterns
- If app process runs but no window appears, verify bundle payload exists under
.../Contents/Resources/app (bun/index.js and views/mainview/*)
- Before debugging build output, kill stale
electrobun dev --watch processes to avoid concurrent rebuild interference
- For macOS app branding, set
build.mac.icons to an .iconset directory (Electrobun generates AppIcon.icns from it)
- For macOS native webviews, style
<select> explicitly (appearance: none + -webkit-appearance: none) to avoid glossy/system glass dropdown chrome regressions
References
references/api.md โ Full API reference (BrowserWindow, RPC, menus, tray, updater)
references/config.md โ electrobun.config.ts schema and build options
references/react-vite.md โ React + Tailwind + Vite HMR setup pattern
references/gotchas.md โ Platform-specific limitations and workarounds