Skip to main content
The desktop integration wraps your ChatJS web app in an Electron shell. The Electron process loads your deployed or local app in a BrowserWindow and adds desktop-specific behavior such as deep-link authentication, system tray support, native window chrome, and auto-updates.

Scaffold with the CLI

When you run npx @chat-js/cli@latest create, the CLI asks:
Include an Electron desktop app? No / Yes
Choosing Yes copies an electron/ subfolder into your project:
my-app/
├── app/
├── electron/
│   ├── src/
│   │   ├── main.ts
│   │   ├── preload.ts
│   │   ├── preload.d.ts
│   │   ├── config.ts
│   │   └── lib/
│   │       └── auth-client.ts
│   ├── build/
│   ├── icon.png
│   ├── electron-builder.config.js
│   ├── entitlements.mac.plist
│   └── package.json
└── chat.config.ts
The generated desktop app derives its app name, protocol scheme, and production URL from chat.config.ts.

Development

Start the web app first, then launch Electron:
# Terminal 1 — web app
bun run dev

# Terminal 2 — Electron
cd electron
bun install
bun run dev
In development, the Electron wrapper points at http://localhost:3000 by default.

Authentication

OAuth redirects cannot return directly to an in-app http://localhost page inside Electron. ChatJS uses @better-auth/electron to bridge the browser sign-in flow back into the desktop app with a custom URL scheme and PKCE.

How it works

The main integration points are:
  • Server: lib/auth.ts enables the Electron Better Auth plugin
  • Web client: lib/auth-client.ts handles the browser-side redirect flow
  • Electron main: src/main.ts sets up deep-link handling and IPC bridges
  • Electron preload: src/preload.ts exposes auth bridges to the renderer

Custom URL Scheme

The desktop auth scheme comes from appPrefix in chat.config.ts.
const config = defineConfig({
  appPrefix: "myapp",
});
That value flows into Electron protocol registration and desktop auth callbacks automatically.

Auto-Updates

The desktop integration uses electron-updater with GitHub Releases. When a packaged app starts, it can check for updates and apply them on a later quit. The GitHub repository used for updates is configured in electron-builder.config.js:
publish: {
  provider: "github",
  owner: "your-github-username",
  repo: "your-repo-name",
  releaseType: "release",
},

Customization

App icon

Replace electron/icon.png with your own 512x512 PNG, then regenerate the platform-specific assets:
cd electron
bun run generate-icons

App name, scheme, and production URL

Edit chat.config.ts in your project root:
const config = defineConfig({
  appName: "My App",
  appPrefix: "myapp",
  appUrl: "https://my-app.vercel.app",
});
Before each build, the Electron prebuild step writes a branding.json file from those values so the desktop app stays aligned with your web app branding.

Building for Distribution

Build desktop installers from the electron/ directory:
cd electron
bun run dist:mac
bun run dist:win
bun run dist:linux
Build outputs are written to electron/release/.
  • macOS: .dmg
  • Windows: .exe installer
  • Linux: .AppImage and .deb
To test the packaged app without an installer, use:
bun run package

Release Flow

In the ChatJS monorepo, desktop releases follow the same version-bump PR model used for package releases:
  1. Add a changeset that includes @chatjs/electron
  2. Let the Changesets workflow open the version PR
  3. Merge that version PR
  4. The Electron release workflow reads the new apps/electron/package.json version, builds macOS, Windows, and Linux artifacts, and publishes them to GitHub Releases
For a concrete reference, see the workflow used in this repository: If you scaffold a new app with the Electron option, you get the desktop app code and build config, but not this repository’s release automation automatically. Use the workflow above as a starting point for your own project if you want the same PR-driven release flow.

macOS Distribution Notes

For public macOS distribution outside the App Store, you will usually want:
  • code signing
  • notarization
  • a stable bundle identifier and install path
The generated app includes the hardened runtime entitlements file, but notarization and signing still require your Apple Developer credentials and CI setup.