Eine Electron-App ist im Kern ein Node-Projekt — und die package.json ist die Schaltzentrale. Hier die Pflichtfelder, die Electron-spezifischen Konventionen und die Konfigurations-Felder für die gängigen Build-Tools.

Minimal-Beispiel

JSON package.json
{
  "name": "my-app",
  "productName": "My App",
  "version": "1.0.0",
  "type": "module",
  "main": "dist/main/main.js",
  "author": "Anna Beispiel <anna@example.com>",
  "homepage": "https://example.com",
  "scripts": {
    "dev": "electron-vite dev",
    "build": "electron-vite build && electron-builder",
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "^32.0.0",
    "electron-builder": "^25.0.0",
    "electron-vite": "^2.3.0"
  },
  "dependencies": {
    "electron-store": "^10.0.0"
  }
}

Pflicht-Felder

main — Einstiegspunkt

Pflicht. Sagt Electron, welche Datei den Main-Prozess startet:

JSON
"main": "dist/main/main.js"

Bei Vanilla-Setup oft "main": "main.js". Bei electron-vite nach Build: "main": "out/main/index.js" oder ähnlich (je nach Konfig).

name und productName

name ist npm-konform (lowercase, no spaces). productName ist das Anzeige-Label:

JSON
"name": "my-app",
"productName": "My App"

Auf macOS taucht productName als App-Name in Menüleiste, Dock und About-Dialog auf. Sorgfältig wählen — User sehen das überall.

version

SemVer. Wird für Auto-Update relevant — die Version im Update-Server muss höher sein als die installierte.

ESM vs. CommonJS

Electron unterstützt seit v28 ESM voll im Main-Prozess. Empfohlen für neue Projekte:

JSON
"type": "module"

Damit funktionieren import/export ohne Build-Tool. Konsequenzen:

  • __dirname und __filename existieren nicht — selbst basteln aus import.meta.url
  • Manche Pakete sind noch CommonJS-only — für die createRequire nutzen
JavaScript ESM-Workarounds
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { createRequire } from 'node:module';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const legacyPackage = require('some-cjs-only-package');

Wer alte Beispiele liest mit const { app } = require('electron'): das ist CommonJS — funktioniert weiter, aber nicht mehr Default-Empfehlung.

Scripts

Übliche Scripts in einem electron-vite + electron-builder Setup:

JSON
"scripts": {
  "start": "electron .",
  "dev": "electron-vite dev",
  "preview": "electron-vite preview",
  "build": "electron-vite build",
  "build:win": "npm run build && electron-builder --win",
  "build:mac": "npm run build && electron-builder --mac",
  "build:linux": "npm run build && electron-builder --linux",
  "lint": "eslint .",
  "test": "vitest"
}

Konvention: dev für Entwicklung mit HMR, build für Production-Bundle, build:<platform> für Distributions-Pakete.

build-Field — electron-builder

electron-builder liest seine Konfiguration aus dem build-Field der package.json (oder aus electron-builder.yml):

JSON
"build": {
  "appId": "com.example.myapp",
  "productName": "My App",
  "directories": {
    "output": "release/"
  },
  "files": [
    "out/**",
    "package.json"
  ],
  "mac": {
    "target": ["dmg", "zip"],
    "category": "public.app-category.utilities"
  },
  "win": {
    "target": "nsis",
    "publisherName": "Anna Beispiel"
  },
  "linux": {
    "target": ["AppImage", "deb"],
    "category": "Utility"
  },
  "publish": [
    {
      "provider": "github",
      "owner": "anna",
      "repo": "my-app"
    }
  ]
}

Wichtige Felder:

  • appId: weltweit eindeutiger Bundle-Identifier (Pflicht für Code-Signing)
  • directories.output: wohin die Pakete geschrieben werden
  • files: was ins Paket muss (alles andere wird ausgelassen)
  • publish: Auto-Update-Server-Konfig

Mehr Details im Building-&-Packaging-Kapitel.

Dependencies vs. devDependencies

Bei Electron mehrdeutiger als bei normalen Node-Apps:

Was reinWohin
electron selbstdevDependencies (wird gebündelt)
electron-builder, electron-vitedevDependencies
Build-Tools, Linter, TesterdevDependencies
Native Module (better-sqlite3)dependencies (müssen ausgeliefert werden)
Pure-JS-Module (electron-store)dependencies
react, Frontend-Libsje nach Bundling — bei Vite/Webpack oft devDependencies (gebündelt)

Faustregel: was vom Bundler ins Bundle eingebaut wird → devDependencies. Was zur Laufzeit per require/import aus node_modules geladen wird → dependencies.

electron-builder packt nur dependencies ins Asset-Verzeichnis — falsch klassifizierte Pakete fehlen plötzlich in der gepackten App.

Auto-Update-relevante Felder

JSON
{
  "version": "1.2.3",
  "homepage": "https://example.com",
  "repository": {
    "type": "git",
    "url": "https://github.com/anna/my-app.git"
  },
  "author": {
    "name": "Anna Beispiel",
    "email": "anna@example.com"
  }
}

repository und homepage werden von electron-updater für GitHub-basiertes Auto-Update verwendet — fehlende Felder führen zu kryptischen Fehlern beim Publish.

Interessantes

main muss auf existierende Datei zeigen — auch in der gepackten App.

Während Dev: main.js. Nach Build: dist/main/main.js o. ä. — nicht beides parallel haben. electron-builder packt den Code nach app.asar; das Ziel von main muss innerhalb existieren.

productName setzen — sonst heißt deine App wie der npm-package-name.

Ohne productName zeigt macOS „my-app" statt „My App" in der Menüleiste. Pflicht-Setzung für jede Production-App.

type: module oder Datei-Endung .mjs.

Modern: "type": "module" global setzen. Wer einzelne CJS-Files braucht: .cjs-Endung. Wer einzelne ESM-Files in einem CJS-Projekt braucht: .mjs. Mischen geht, aber wird schnell unübersichtlich.

Falsche Dependency-Klassifizierung = fehlende Pakete im Build.

Native Module wie better-sqlite3 als devDependencies markiert: nach Build crasht die App, weil das Modul nicht im Bundle ist. Im Zweifel: alles, was zur Laufzeit gebraucht wird, in dependencies.

appId ist Pflicht für Code-Signing.

Reverse-DNS-Format: com.example.myapp. Muss eindeutig sein — auf macOS landet das im Bundle-Identifier, was für Notarization und Auto-Update kritisch ist.

files sparsam — sonst wird das Paket riesig.

Default packt electron-builder fast alles, inklusive node_modules. Mit files-Array gezielt einschließen (out/**, package.json). Tests, Source-Maps, README ausschließen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Grundlagen

Zur Übersicht