Electron kombiniert Chromium und Node.js, damit du mit HTML, CSS und JavaScript vollwertige Desktop-Apps bauen kannst. VS Code, Slack und Discord nutzen es bereits, und du kannst das auch! ⚡
Was ist Electron? 🤔
Stell dir vor, du baust eine Webanwendung, aber statt im Browser läuft sie als eigenständige Desktop-App auf Windows, macOS und Linux. Genau das macht Electron möglich. Im Grunde packt Electron einen Chromium-Browser und eine Node.js-Runtime in ein Paket zusammen. Deine App hat also vollen Zugriff auf Web-APIs und auf das Dateisystem, Betriebssystem-Features und native Module.
Das Ganze wurde ursprünglich von GitHub für den Atom-Editor entwickelt und ist mittlerweile das go-to Framework für Cross-Platform Desktop-Apps.

Wie funktioniert Electron unter der Haube? ⚙️
Electron basiert auf zwei Kernkomponenten:
- Chromium, rendert deine UI (HTML/CSS/JS), genau wie im Browser
- Node.js, gibt dir Zugriff auf das Dateisystem, Netzwerk, native Module und mehr
Jede Electron-App besteht aus zwei Arten von Prozessen:
Main Process 🌱
Der Main Process ist quasi der “Boss”. Er startet die App, erstellt Fenster, verwaltet den App-Lifecycle und hat vollen Node.js-Zugriff. Es gibt pro App genau einen Main Process.
Renderer Process 🎨
Jedes Fenster (BrowserWindow) bekommt seinen eigenen Renderer Process. Das ist im Grunde ein Chromium-Tab. Hier läuft dein Frontend-Code. Aus Sicherheitsgründen sollte der Renderer keinen direkten Node.js-Zugriff haben.
Deine erste Electron-App aufsetzen 🚀
Lass uns direkt loslegen. Erstelle ein neues Projekt:
mkdir my-electron-app && cd my-electron-app
npm init -y
npm install electron --save-devDeine package.json sollte so aussehen:
{
"name": "my-electron-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder"
},
"devDependencies": {
"electron": "^33.0.0",
"electron-builder": "^25.0.0"
}
}Jetzt die main.js, das Herz deiner App:
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
});
win.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});Beachte die webPreferences: contextIsolation: true und nodeIntegration: false sind extrem wichtig für die Sicherheit. Dazu später mehr.

Preload Scripts, Die sichere Brücke 🛡️
Der Preload-Script ist der sichere Vermittler zwischen Main und Renderer Process. Er läuft im Kontext des Renderers, hat aber Zugriff auf einige Node.js-APIs:
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
// Sichere API für den Renderer
getSystemInfo: () => ipcRenderer.invoke('get-system-info'),
openFile: () => ipcRenderer.invoke('dialog:openFile'),
onUpdateAvailable: (callback) => {
ipcRenderer.on('update-available', (_event, info) => callback(info));
},
saveData: (data) => ipcRenderer.invoke('save-data', data)
});Mit contextBridge.exposeInMainWorld stellst du nur die Funktionen bereit, die der Renderer wirklich braucht. Kein direkter Node.js-Zugriff, keine Sicherheitslücken.
IPC-Kommunikation, Main & Renderer reden miteinander 💬
IPC (Inter-Process Communication) ist das Rückgrat jeder Electron-App. So kommunizieren Main und Renderer Process sicher miteinander:
// main.js - IPC Handler registrieren
const { ipcMain, dialog } = require('electron');
const os = require('os');
const fs = require('fs');
ipcMain.handle('get-system-info', () => {
return {
platform: os.platform(),
arch: os.arch(),
cpus: os.cpus().length,
totalMemory: Math.round(os.totalmem() / 1024 / 1024 / 1024) + ' GB',
nodeVersion: process.versions.node,
electronVersion: process.versions.electron
};
});
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text Files', extensions: ['txt', 'md', 'json'] }]
});
if (canceled) return null;
return fs.readFileSync(filePaths[0], 'utf-8');
});
ipcMain.handle('save-data', async (_event, data) => {
const { canceled, filePath } = await dialog.showSaveDialog({
filters: [{ name: 'JSON', extensions: ['json'] }]
});
if (canceled || !filePath) return false;
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
return true;
});Und im Renderer nutzt du die exponierte API:
// renderer.js
async function showSystemInfo() {
const info = await window.electronAPI.getSystemInfo();
const container = document.getElementById('system-info');
// Bestehende Inhalte entfernen
while (container.firstChild) {
container.removeChild(container.firstChild);
}
Object.entries(info).forEach(([key, value]) => {
const p = document.createElement('p');
const strong = document.createElement('strong');
strong.textContent = key + ': ';
p.appendChild(strong);
p.appendChild(document.createTextNode(String(value)));
container.appendChild(p);
});
}
document.getElementById('btn-sysinfo')
.addEventListener('click', showSystemInfo);
document.getElementById('btn-open')
.addEventListener('click', async () => {
const content = await window.electronAPI.openFile();
if (content) {
document.getElementById('file-content').textContent = content;
}
});TypeScript nutzen 💪
Electron und TypeScript sind ein Dream-Team. TypeScript gibt dir Autovervollständigung, Typ-Prüfungen und macht deinen Code deutlich robuster. Gerade bei IPC-Kommunikation, wo du Daten zwischen Prozessen hin- und herschickst, sind Typen Gold wert.
// types.ts - Gemeinsame Typen für Main und Renderer
interface SystemInfo {
platform: string;
arch: string;
cpus: number;
totalMemory: string;
nodeVersion: string;
electronVersion: string;
}
interface ElectronAPI {
getSystemInfo: () => Promise<SystemInfo>;
openFile: () => Promise<string | null>;
saveData: (data: unknown) => Promise<boolean>;
onUpdateAvailable: (callback: (info: UpdateInfo) => void) => void;
}
declare global {
interface Window {
electronAPI: ElectronAPI;
}
}Packaging mit electron-builder 📦
Wenn deine App fertig ist, willst du sie als installierbare Anwendung verteilen. electron-builder ist dafür das Standardwerkzeug:
{
"build": {
"appId": "com.example.myapp",
"productName": "My Electron App",
"directories": {
"output": "dist"
},
"win": {
"target": ["nsis", "portable"],
"icon": "assets/icon.ico"
},
"mac": {
"target": ["dmg", "zip"],
"icon": "assets/icon.icns",
"category": "public.app-category.developer-tools"
},
"linux": {
"target": ["AppImage", "deb"],
"icon": "assets/icon.png",
"category": "Development"
},
"publish": [
{
"provider": "github",
"owner": "dein-username",
"repo": "dein-repo"
}
]
}
}# Bauen für die aktuelle Plattform
npm run build
# Für alle Plattformen (erfordert ggf. spezielle Umgebung)
npx electron-builder --win --mac --linuxAuto-Updates 🔄
Eine der coolsten Features von Electron ist die Möglichkeit, automatische Updates auszuliefern. Mit electron-updater (Teil von electron-builder) geht das so:
// main.js - Auto-Update Setup
const { autoUpdater } = require('electron-updater');
app.whenReady().then(() => {
createWindow();
// Updates prüfen
autoUpdater.checkForUpdatesAndNotify();
});
autoUpdater.on('update-available', (info) => {
// Renderer benachrichtigen
mainWindow.webContents.send('update-available', info);
});
autoUpdater.on('update-downloaded', (info) => {
// Update installieren und App neustarten
autoUpdater.quitAndInstall();
});Damit das funktioniert, musst du deine Releases auf GitHub (oder einem anderen Provider) hosten. electron-builder kümmert sich um den Rest.
Sicherheit, Das musst du wissen 🔒
Sicherheit ist bei Electron kritisch, weil deine App vollen Systemzugriff hat. Hier die wichtigsten Regeln:
| Einstellung | Empfehlung | Warum? |
|---|---|---|
nodeIntegration | false | Verhindert direkten Node.js-Zugriff im Renderer |
contextIsolation | true | Isoliert Preload-Script vom Webinhalt |
sandbox | true | Zusätzliche Prozess-Isolation |
webSecurity | true | Aktiviert Same-Origin-Policy |
Zusätzliche Tipps:
- Validiere IPC-Inputs, Vertraue niemals Daten aus dem Renderer
- Lade keine Remote-Inhalte ohne Content Security Policy
- Halte Electron aktuell, Chromium-Updates patchen Sicherheitslücken
- Nutze keine
shell.openExternal()mit unvalidierten URLs
Performance-Tipps ⚡
Electron-Apps haben den Ruf, Ressourcen-hungrig zu sein. Mit diesen Tipps holäst du das Maximum raus:
- Lazy Loading, Lade Module erst wenn sie gebraucht werden
- BrowserWindow sinnvoll nutzen, Nicht benötigte Fenster schließen oder
win.hide()verwenden - V8 Snapshots, Startup-Zeit mit
v8-compile-cacheverkürzen - DevTools in Production deaktivieren
- Background Throttling,
backgroundThrottling: truefür nicht-sichtbare Fenster - Asar-Packaging, App-Dateien in ein asar-Archiv packen für schnelleres Laden
// Lazy Loading Beispiel
async function loadHeavyModule() {
const { heavyFunction } = await import('./heavy-module.js');
return heavyFunction();
}
// Statt sofort beim Start alles zu laden
app.whenReady().then(async () => {
createWindow();
// Schwere Module erst nach dem Fenster laden
setTimeout(async () => {
const analytics = await import('./analytics.js');
analytics.init();
}, 2000);
});Node.js-Backend-Integration 🖥️
Da Electron Node.js eingebaut hat, kannst du direkt Backend-Logik in deiner App nutzen. Wenn du allerdings einen richtigen Backend-Server brauchst, zum Beispiel für eine API oder Datenbankanbindung, kann NestJS eine super Ergänzung sein:
Wiederverwendbare UI-Komponenten 🧱
Ein großer Vorteil von Electron: Du kannst deine UI-Komponenten zwischen Web-App und Desktop-App teilen. Web Components eignen sich hier besonders gut, weil sie framework-agnostisch sind:
Electron vs. Tauri, Der Vergleich 🥊
Tauri ist der aufstrebende Herausforderer. Hier ein ehrlicher Vergleich:
| Kriterium | Electron | Tauri |
|---|---|---|
| Sprache (Backend) | JavaScript/Node.js | Rust |
| Rendering Engine | Chromium (gebundelt) | System WebView |
| Bundle-Größe | ~150 MB+ | ~5-10 MB |
| RAM-Verbrauch | Höher | Niedriger |
| Ökosystem | Riesig, ausgereift | Wachsend |
| Lernkurve (Web-Devs) | Niedrig | Mittel (Rust) |
| Plattformen | Win/Mac/Linux | Win/Mac/Linux/Mobile |
| Stabilität | Battle-tested | Gut, aber jünger |
Meine Einschätzung: Wenn du aus der Web-Welt kommst und schnell loslegen willst, ist Electron die sichere Wahl. Wenn Bundle-Größe und Performance kritisch sind und du bereit bist, Rust zu lernen, ist Tauri extrem spannend.
Real-World Beispiele 🌎
Diese Apps nutzen Electron und du nutzt sie vermutlich täglich:
- VS Code, Der populärste Code-Editor überhaupt
- Slack, Team-Kommunikation
- Discord, Gaming & Community Chat
- Obsidian, Notizen und Wissensmanagement
- Figma Desktop, Design-Tool
- Notion Desktop, Produktivität
- 1Password, Passwort-Manager
Das zeigt: Electron ist absolut production-ready für Apps jeder Größe.
Fazit 🎯
Electron ist nach wie vor das mächtigste Framework für Cross-Platform Desktop-Apps, wenn du aus der Web-Welt kommst. Ja, die Bundle-Größe ist größer als bei nativen Apps, und der RAM-Verbrauch ist höher. Aber dafür bekommst du:
- Das gesamte Web-Ökosystem (npm, Frameworks, Tools)
- Eine riesige Community und Dokumentation
- Battle-tested in Millionen von Installationen
- Schnelle Entwicklung mit bekannten Technologien
Probier es aus, mit den Sicherheits- und Performance-Tipps aus diesem Artikel bist du bestens aufgestellt! 🚀