# How to Run OmniRoute as an Electron Desktop App: Complete Setup Guide

> Learn how to run OmniRoute as an Electron desktop app with this complete setup guide. Integrate Next.js, system tray controls, and auto-updates seamlessly.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-01

---

**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:

```bash
npm run build

```

Next, install the Electron-specific dependencies:

```bash
cd electron
npm install

```

In a separate terminal, start the Next.js development server:

```bash
npm run dev

```

Finally, launch Electron in development mode:

```bash
cd electron
npm run dev

```

According to the source code in [`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

```bash
npm run build

```

Start the packaged Electron app:

```bash
cd electron
npm start

```

This executes `electron .` and triggers `startNextServer()` in [`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json) (lines 15-24).

Navigate to the Electron directory:

```bash
cd electron

```

Build for specific platforms:

```bash

# 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 `resolveNodeExecutable` with custom Node options and waiting for the health endpoint before showing the UI.
- **Distribution builds** use `electron-builder` via `npm run build:win`, `build:mac`, or `build:linux` to generate installers in `dist-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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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.