How to Run OmniRoute as an Electron Desktop App: Complete Setup Guide
OmniRoute provides a self-contained Electron wrapper in the electron/ folder that launches the Next.js server in headless mode, embeds it in a native desktop window, and exposes system tray controls, auto-updates, and secure IPC bridges.
OmniRoute is an open-source routing platform that ships with a first-class Electron desktop application. This guide explains how to run OmniRoute as an Electron desktop app using the source code from the diegosouzapw/OmniRoute repository, covering development hot-reload workflows, production execution, and cross-platform packaging.
Development Mode: Hot-Reload Workflow
To run OmniRoute as an Electron desktop app with hot-reload support, you must simultaneously run the Next.js development server and the Electron shell.
First, build the Next.js application from the repository root:
npm run build
Next, install the Electron-specific dependencies:
cd electron
npm install
In a separate terminal, start the Next.js development server:
npm run dev
Finally, launch Electron in development mode:
cd electron
npm run dev
According to the source code in electron/main.js line 66, development mode automatically opens Chrome DevTools and disables auto-updates. The main process polls the Next.js server using waitForServer() (lines 71-87) before rendering the UI to prevent a blank screen during long database migrations.
Production Mode: Running the Packaged App
To run OmniRoute as a standalone Electron desktop application, build the Next.js app in standalone mode and launch the Electron main process.
Build the production Next.js bundle:
npm run build
Start the packaged Electron app:
cd electron
npm start
This executes electron . and triggers startNextServer() in electron/main.js (lines 15-22), which spawns a Node binary using resolveNodeExecutable with custom NODE_OPTIONS and NODE_PATH (lines 71-95). The main process captures server stdout/stderr via stdio: 'pipe' for logging and polls the /api/monitoring/health endpoint via waitForServer() before displaying the window.
Building Installers for Distribution
To create distributable installers for Windows, macOS, and Linux, use the platform-specific build scripts defined in electron/package.json (lines 15-24).
Navigate to the Electron directory:
cd electron
Build for specific platforms:
# Windows (NSIS installer)
npm run build:win
# macOS (Universal DMG)
npm run build:mac
# Linux (AppImage and DEB)
npm run build:linux
The Electron Builder configuration outputs installers to the dist-electron/ directory. These commands first execute prepare:bundle to ensure the Next.js standalone build is ready, then package the application with native assets from electron/assets/ (including icon.ico, icon.icns, and tray-icon.png).
Core Architecture and IPC Communication
Understanding the Electron architecture helps when debugging or extending the desktop functionality.
Main Process (electron/main.js)
The main process handles window creation, system tray management, and the embedded Next.js server lifecycle. It implements a disposer pattern for IPC listeners to guarantee precise cleanup of event handlers and sets Content-Security-Policy headers via Electron session headers to restrict script sources per best practices.
Preload Script (electron/preload.js)
The preload script exposes a secure bridge at window.electronAPI that allows the React frontend to communicate with the main process without enabling nodeIntegration. This bridge forwards methods like getAppInfo, openExternal, and restartServer.
React Hooks (src/shared/hooks/useElectron.ts)
The React frontend consumes the preload bridge through hooks such as useIsElectron, useElectronAppInfo, and useWindowControls. These hooks use useSyncExternalStore to guarantee zero re-renders and expose loading and error states (lines 12-30).
Available IPC Channels
| Channel | Direction | Description |
|---|---|---|
get-app-info |
Renderer → Main | Returns metadata { name, version, platform, isDev, port } |
open-external |
Renderer → Main | Opens validated URLs in the default system browser |
restart-server |
Renderer → Main | Gracefully restarts the embedded Next.js server |
server-status |
Main → Renderer | Emits server state changes ({ status, port }) |
port-changed |
Main → Renderer | Notifies UI of port changes from the tray menu |
Summary
- OmniRoute provides a complete Electron wrapper in the
electron/folder with separate concerns for the main process, preload bridge, and React hooks. - Development mode requires running both the Next.js dev server and Electron simultaneously, with automatic DevTools opening and server readiness polling via
waitForServer(). - Production mode embeds the Next.js server directly, spawning it via
resolveNodeExecutablewith custom Node options and waiting for the health endpoint before showing the UI. - Distribution builds use
electron-buildervianpm run build:win,build:mac, orbuild:linuxto generate installers indist-electron/. - IPC communication flows through a secure preload bridge exposing
window.electronAPI, enabling safe communication between the React frontend and native desktop features.
Frequently Asked Questions
How do I enable hot-reload during Electron development?
Run npm run dev from the repository root to start the Next.js server on port 3000, then run npm run dev from the electron/ directory to launch Electron with the --no-sandbox flag and automatic DevTools. The Electron main process connects to the dev server instead of the embedded production build, allowing instantaneous updates to both the renderer and main process code.
Why does the Electron window stay blank on first launch?
The main process in electron/main.js implements a waitForServer() polling mechanism (lines 71-87) that waits for the /api/monitoring/health endpoint to respond before rendering the UI. This prevents displaying a blank screen during initial database migrations or slow server startups, with server output captured via stdio: 'pipe' for debugging purposes.
Can I customize the server port when running the Electron app?
Yes. The system tray menu (managed in electron/main.js) allows users to change the server port dynamically. When modified, the main process emits a port-changed event via IPC to notify the React frontend, and the restart-server channel can gracefully restart the embedded Next.js server with the new configuration.
What is the purpose of the preload script?
The electron/preload.js file creates a secure context bridge that exposes specific IPC channels through window.electronAPI to the renderer process. This architecture follows Electron security best practices by keeping nodeIntegration disabled while still allowing the React frontend to access native features like opening external URLs, reading app metadata, and controlling window state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →