Innov8ProTech
ElectronTechnologies / Electron

Desktop apps,
built with web tools.

One React codebase, three platforms — Windows, macOS, and Linux. Signed installers, auto-updates, and native integrations where needed. Below is a real project structure from a production Electron app.

Electron 31+
TypeScript end to end
Signed & notarized
main/window.ts
export function createWindow() {
  const win = new BrowserWindow({
    width: 1280,
    height: 800,
    titleBarStyle: 'hiddenInset',
    webPreferences: {
      preload: path.join(__dirname, '../preload/bridge.js'),
      contextIsolation: true,   // secure by default
      nodeIntegration: false,   // renderer can't touch Node
      sandbox: true,            // OS-level sandbox
    },
  });

  win.loadURL(process.env.VITE_DEV_SERVER_URL
    ?? 'app://index.html');

  return win;
}

3

Platforms from one codebase

~120 MB

Typical installed size

60fps

Chromium rendering

5–10 d

Engineer placement

The stack

Tools we ship with.

Electron 31+

Runtime

electron-vite

Build tooling

electron-builder

Packaging

Forge

Alternative packaging

TypeScript

End to end

React

Renderer layer

Zod

IPC validation

electron-updater

Auto-updates

Playwright

E2E tests

Vitest

Unit tests

Sentry

Crash reporting

Notarize

Apple signing

Security posture

Secure by default. Not bolted on later.

Almost every Electron app that gets compromised skipped one of these. Every project we ship starts with them enforced in the scaffold.

contextIsolation: true

critical

Isolated context for preload scripts. Non-negotiable.

nodeIntegration: false

critical

Renderer can't touch Node directly. Always off.

sandbox: true

critical

OS-level sandbox for every renderer. Default on.

webSecurity: true

critical

Same-origin policy enforced. No exceptions.

CSP on every window

critical

Content Security Policy headers set. Blocks injection attacks.

No remote module

critical

Deprecated and dangerous. Never enabled.

Whitelisted IPC channels

critical

Every channel explicitly declared. No wildcards.

Typed IPC payloads

Zod schemas validate every message. Both sides.

Minimal preload surface

Expose only what the renderer needs. Nothing more.

No remote content in privileged windows

External URLs go to the OS browser or a sandboxed view.

will-navigate locked down

Navigation handlers block unexpected URLs.

Signed updates verified

Auto-update payloads checked against signature before install.

If any of these are missing, your app is at risk

Electron's default settings are safe — but frameworks, tutorials, and legacy code often disable them for convenience. We enforce them at the scaffold level so they can't be turned off by accident.

The process model

Three processes. One app.

Electron isn't one process — it's a small system of them. Understanding the split is the difference between a clean app and a fragile one.

Node.js

Main process

  • App lifecycle
  • Native APIs (dialogs, tray, menus)
  • IPC handlers
  • Auto-updater
  • Global shortcuts

Chromium

Renderer process

  • Your React/Vue/Svelte app
  • Sandboxed by default
  • No Node access
  • One per window
  • Isolated context

Node.js (sandboxed)

Utility process

  • Heavy compute
  • File parsing
  • Background work
  • Image processing
  • Isolated from UI

Typed IPC

Every channel typed. Every payload validated.

IPC is where Electron apps get messy. Channels named by string literals, payloads with no shape, errors that arrive as mystery objects. We don't do that.

  • Renderer can't reach Node directly — preload is the only bridge
  • Every channel has a name, a schema, and a handler
  • Payloads validated with Zod on both sides
  • Errors structured, not thrown as strings
  • TypeScript types shared across all three processes
  • No `send('do-anything', ...)` patterns anywhere
schemas.ts · ipc.ts · bridge.ts · App.tsxTypeScript
// packages/shared/schemas.ts
import { z } from 'zod';

export const InvoiceSchema = z.object({
  customerId: z.string().uuid(),
  amount: z.number().positive(),
  currency: z.enum(['GHS', 'USD', 'EUR']),
});

// main/ipc.ts
ipcMain.handle('invoice:create', async (_e, input) => {
  const data = InvoiceSchema.parse(input); // runtime validation
  return invoiceService.create(data);
});

// preload/bridge.ts
contextBridge.exposeInMainWorld('api', {
  invoices: {
    create: (data) => ipcRenderer.invoke('invoice:create', data),
  },
});

// renderer/App.tsx — typed, safe, validated
await window.api.invoices.create({
  customerId: '...',
  amount: 500,
  currency: 'GHS',
});

Reference architecture

What a production Electron project looks like.

Click any file to see its role and the actual code we ship. Same structure as our internal starter template.

nine9/electron-starterclick any file

repository

index.tsts
1import { app, BrowserWindow, ipcMain } from 'electron';
2import { createWindow } from './window';
3import { registerIpcHandlers } from './ipc';
4 
5app.whenReady().then(() => {
6 registerIpcHandlers(ipcMain);
7 createWindow();
8 
9 app.on('activate', () => {
10 if (BrowserWindow.getAllWindows().length === 0) {
11 createWindow();
12 }
13 });
14});
15 
16app.on('window-all-closed', () => {
17 if (process.platform !== 'darwin') app.quit();
18});

why this file exists

Entry point. Creates the main window, registers IPC handlers, handles app lifecycle.

Honest comparison

Electron vs Tauri vs NativePHP.

We build all three. The right choice depends on your team, your stack, and what you're optimizing for.

Electron

Best fit

Node backend · JS-first

Bundle

~120 MB

Backend

Node.js

Learning

Zero — if you know JS

  • JavaScript/TypeScript-first team
  • Largest ecosystem of native modules
  • Existing web codebase to share
  • Bundle size not the top constraint
  • Broadest community and tooling

Tauri

Rust backend · smallest bundle

Bundle

~10 MB

Backend

Rust

Learning

Steep — Rust required

  • Bundle size is critical
  • Performance is the top concern
  • Team comfortable with Rust
  • Tightest security surface matters
  • Modern architecture is a priority
Explore Tauri

NativePHP

PHP backend · Laravel-native

Bundle

~120 MB

Backend

PHP (Laravel)

Learning

Zero — if you know PHP

  • Existing Laravel app to leverage
  • Team writes PHP, not JS/Rust
  • Desktop companion to a web product
  • One language across the stack
  • You want to stay in the Laravel ecosystem
Explore NativePHP

Bundle size

What the installer actually weighs.

Electron bundles Node and Chromium — same footprint class as NativePHP. Tauri is an order of magnitude smaller. For enterprise internal tools, it rarely matters. For consumer apps downloaded on mobile data, it does.

Native (Swift / WinUI)~5 MB
Tauri~10 MB
Electron~120 MB
NativePHP~120 MB

Renderer-agnostic

Bring whatever you already use.

The renderer is a Chromium window. React, Vue, Svelte, vanilla — if it renders in Chrome, it runs here. Usually your existing web app runs with minimal changes.

React

Largest ecosystem, best tooling

Vue

Lower ceremony, great DX

Svelte

Smallest runtime, fastest startup

Vanilla + Vite

Zero framework tax

Your existing app

Same codebase as your web

Any Chromium target

If it renders in Chrome, it runs here

Performance

Fast cold start. Small memory. Real numbers.

Electron gets a bad rap for bloat. The fixes are well-known — we apply them by default.

V8 snapshot

Pre-compile JavaScript for 30–50% faster cold start.

ASAR packing

Files bundled into a single archive — faster access, tamper-resistant.

Lazy window creation

Don't open all windows at launch. Create on demand.

Renderer pooling

Reuse renderer processes instead of recreating them.

Chromium flags

Disable unused features — geolocation, Bluetooth, sensors.

Startup budgets

Cold start < 2s, warm start < 500ms, window ready < 1s.

< 2s

Cold start target

< 500ms

Warm start

60–100 MB

Base memory

40–80 MB

Per renderer window

Native modules

When JavaScript can't reach.

Most Electron apps never need native code. When they do — a specific USB device, an unusual codec, hardware integration — we write it.

N-API / node-addon-api

Modern C++ addon interface. ABI-stable across Node versions.

Rust via napi-rs

Modern, memory-safe native addons. Best of both worlds.

Prebuilt binaries

Ship without forcing users to compile. Faster installs, fewer failures.

@electron/rebuild

Auto-rebuild native modules against Electron's Node version.

Testing

Modern tests. Real coverage.

Spectron is dead. We use the modern Electron testing stack — Playwright for E2E, Vitest for main process units, and a CI matrix across all three platforms.

Playwright

Official Electron support. End-to-end tests, screenshots, video.

Vitest

Main process unit tests. Fast, minimal config, TypeScript native.

WebdriverIO

Alternative E2E framework with broad Electron support.

GitHub Actions matrix

CI on Windows, macOS, and Linux in parallel.

Signing in CI

Automated code signing and notarization on release.

Visual regression

Playwright snapshots catch pixel-level UI regressions.

Distribution & signing

Signed on all three platforms.

Unsigned installers get blocked by users. Every app we ship is properly signed, notarized, and distributed with an auto-update feed.

Windows

  1. 01Authenticode certificate from a CA
  2. 02Sign .exe and NSIS installer
  3. 03Build SmartScreen reputation over time
  4. 04Ship as NSIS installer, MSI, or Squirrel

macOS

  1. 01Apple Developer ID certificate
  2. 02Enable hardened runtime
  3. 03Notarize with Apple's notary service
  4. 04Staple ticket, distribute as .dmg or .zip

Linux

  1. 01AppImage for portable distribution
  2. 02.deb for Debian/Ubuntu
  3. 03.rpm for Fedora/RHEL
  4. 04Snap and Flatpak for sandboxed installs

Auto-update flow

Release to installed in five steps.

01

Release

New version published to signed release feed.

02

Check

Installed apps poll the feed on schedule.

03

Download

Delta download where possible — smaller payload.

04

Verify

Signature verified before install. Rejects tampering.

05

Install

Runs on next launch, or forced if critical.

Release channels

Four tracks, one feed.

Stable

Production users. Tested, signed, released.

Beta

Opt-in testers. New features, rough edges okay.

Canary

Nightly builds. Bleeding edge, may break.

Enterprise LTS

Pinned major version with security backports for 18 months.

Real-world use

What teams actually build.

Developer tools

IDEs, terminals, database clients, API explorers. The VS Code class of app.

WinMacLinux

Communication apps

Slack-class chat, voice, and collaboration tools. WebRTC, notifications, tray.

WinMacLinux

Design & media

Figma-class design tools, video editors, audio workstations. Canvas, GPU, media codecs.

WinMac

Enterprise desktop

Internal admin tools for private networks. Deployed via MDM, GPO, or SCCM.

WinMacLinux

POS & kiosk

Retail terminals, self-service stations. Hardware integration, locked-down mode.

WinLinux

Desktop companions

Desktop app to a SaaS product. Shared codebase, one team, one deployment.

WinMacLinux

In the box

What ships with the app.

Node.js runtime

Bundled Node — user installs nothing separately.

Chromium + V8

Full Chromium engine with the V8 JavaScript runtime.

Your app (ASAR packed)

All your code packaged into a single .asar archive.

Preload script

The bridge between renderer and main. Minimal, typed.

Auto-updater

Signed release feed with delta downloads.

Native modules

Any C++/Rust addons your app needs, prebuilt.

The process

Five phases. Every Electron build follows them.

01

Discovery

Platform targets, security model, native integrations, and shared-code strategy. Architecture locked before any code.

Deliverables

Platform matrix · Security model · Feature scope · Fixed-price quote

02

Shell & security

Project scaffolded with contextIsolation, sandbox, typed IPC, and a strict preload bridge. Secure by default.

Deliverables

Project scaffold · Typed IPC · Preload bridge · CSP policy

03

Build

Main process, renderer, and shared packages in parallel. Weekly builds to your testers on all three platforms.

Deliverables

Working builds · Weekly demos · Documentation · Preview channel

04

Signing & packaging

Authenticode, Developer ID, AppImage/deb/rpm. Installers tested on clean machines. Update flow validated.

Deliverables

Signed installers · Notarization · Update feed · Install report

05

Launch & handover

Release pipeline live, crash reporting wired, auto-updates flowing. Your team walks away with everything.

Deliverables

Production release · Crash reporting · Update pipeline · Full IP

Timelines

How long an Electron build takes.

Simple

6–8 wk

  • Single-platform launch
  • Standard windows and menus
  • Basic security posture
  • One renderer target

Feature-rich

10–14 wk

  • Windows + macOS + Linux
  • Complex UI and native integrations
  • Auto-update pipeline
  • Full security hardening

Complex

16–20 wk

  • Multi-window, tray, global shortcuts
  • Native modules (C++/Rust)
  • Enterprise deployment (MDM/SCCM)
  • Custom auto-update channels

Honest advice

When Electron fits.
When it doesn't.

Choose Electron when

  • Your team is JavaScript/TypeScript-first
  • You already have a web codebase to share
  • Bundle size isn't the top constraint
  • You need the broadest native module ecosystem
  • Community size and long-term support matter

Consider alternatives when

  • Bundle size is critical (Tauri is 10× smaller)
  • Your team is Rust-first and wants the tightest surface
  • You have a Laravel app (NativePHP is a better fit)
  • You need mobile from the same codebase
  • Performance-critical native UI is the main use case

Selected work

Electron apps in production.

Developer tools

Cross-platform IDE

A code editor for a niche language, shipped on all three platforms with a built-in terminal, debugger, and extension marketplace.

Outcome

3 platforms, 1 codebase

Enterprise

Internal admin for a healthcare network

Desktop admin app deployed across 400 workstations via MDM. Private network, no browser, hardware badge reader.

Outcome

400 deployments

SaaS companion

Desktop app for a project management platform

Offline-capable desktop companion sharing 65% of code with the web app. Notifications, global shortcuts, tray.

Outcome

65% shared code

Client feedback

What our clients
actually say.

The move to SikaNet CBS was a bigger shift than I expected — but the right one. Before, our Susu collectors were on paper and every BOG report took us days to assemble. Now collections appear in the office before a collector even finishes their route, and the reports are just there. The team understood our business from day one.

DA

David Awuni

Founder, Adwumapa Microfinance

We were cautious about moving patient records online. The on-prem approach changed that — everything stays inside the health center, nothing goes to a foreign cloud. Our clinicians now retrieve a file in seconds instead of ten minutes, and every entry has an audit trail behind it.

MD

Medical Director

Sekyedumase Health Center

The patient management system they developed has streamlined our ophthalmology department. Appointment scheduling, patient records, and referral tracking are now seamless — saving us hours of administrative work every day.

IA

Isaac Adu

Ophthalmologist, Ghana Health Service

FAQ

Electron
questions.

Still curious?
Electron when you want the ecosystem and maturity — everything works, every native module exists, and your React team is already productive in it. Tauri for smaller bundles and if you're comfortable with Rust on the backend.

Let us talk

Building for desktop?

We can help — whether you need a full team or just one engineer to get you to launch.