Electron — Apps desktop avec les technos web
Electron combine un moteur Chromium (rendu HTML/CSS/JS) et Node.js (accès système) dans un seul runtime. Une application web devient une application de bureau native — Windows, macOS et Linux simultanément.
Définition & fonctionnement
Electron est un framework open source créé par GitHub qui permet de construire des applications de bureau multiplateformes avec des technologies web standard (HTML, CSS, JavaScript). Il combine deux moteurs distincts dans un seul processus :
Chromium gère le rendu de l'interface (le même navigateur que Google Chrome, donc compatibilité CSS3/ES2024 complète), et Node.js fournit l'accès au système d'exploitation (fichiers, réseau, processus). Les deux communiquent via un mécanisme appelé IPC (Inter-Process Communication).
Différence fondamentale avec HTA
| Critère | HTA | Electron |
|---|---|---|
| Moteur HTML | MSHTML / IE | Chromium (Chrome) |
| JavaScript | ES5 seulement | ES2024 complet |
| CSS | IE11 limité | CSS3 complet + CSS Grid |
| Plateformes | Windows uniquement | Win + macOS + Linux |
| Taille du bundle | ~0 Ko (1 fichier) | ~150–300 Mo |
| Installation requise | Aucune | Oui (installeur) |
| npm / node_modules | Non requis | Requis |
Historique & évolution
GitHub crée un framework interne pour construire l'éditeur de code Atom. Il s'appelle alors "Atom Shell" et combine Node.js et le moteur WebKit (Chromium).
Atom Shell est rendu open source et renommé Electron. Microsoft adopte rapidement le framework pour construire VS Code, ce qui propulse sa popularité.
Slack, Discord, Figma, Postman, Twitch rejoignent l'écosystème Electron. Le framework devient le standard de facto pour les apps desktop cross-platform écrites par des équipes web.
GitHub (et donc Electron) rejoint Microsoft. Le développement s'accélère,
la sécurité est renforcée avec l'introduction de contextIsolation
et les preload scripts.
nodeIntegration désactivé par défaut, contextIsolation
activé par défaut, introduction de l'API contextBridge pour
exposer des APIs sécurisées au renderer.
L'essor de Tauri (Rust, ~5 Mo) met en question le modèle lourd d'Electron. L'équipe répond avec des améliorations de performance et de partage de processus Chromium entre fenêtres.
Architecture biprocessus
Electron fonctionne avec deux types de processus séparés, chacun avec des responsabilités et des accès distincts. C'est le concept le plus important à comprendre.
Node.js complet
BrowserWindow
app, ipcMain
dialog, shell
Menu, Tray
Accès fichiers, OS
Chromium (Chrome)
ES2024 + CSS3
React / Vue / Svelte
ipcRenderer
DOM complet
Node.js : limité*
* Node.js est désactivé dans le Renderer par défaut depuis Electron 10 (bonne pratique sécurité). L'accès système passe par un preload script.
mon-app/ ├── package.json ├── main.js ← Main Process (Node.js) ├── preload.js ← Pont sécurisé main ↔ renderer └── renderer/ ├── index.html ├── styles.css └── app.js ← Renderer Process (Chromium)
Installation & démarrage
# Créer un nouveau projet mkdir mon-app && cd mon-app npm init -y # Installer Electron npm install --save-dev electron # Ou utiliser le template officiel (recommandé) npm create electron-vite@latest cd mon-app && npm install # Lancer en mode développement npm run dev
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, // sécurité : obligatoire nodeIntegration: false // sécurité : désactiver } }) win.loadFile('renderer/index.html') } app.whenReady().then(createWindow)
APIs Node.js disponibles
Dans le Main Process, l'intégralité des modules Node.js est disponible :
fs / fs.promisesLire, écrire, copier, déplacer, supprimer des fichiers. API synchrone ou async/await.
pathManipulation des chemins de fichiers de façon cross-platform (séparateurs Windows/Unix).
child_processLancer des processus externes (ffmpeg, git, python…), capturer stdout/stderr en temps réel.
net / http / httpsServeur HTTP, requêtes, WebSockets, gestion réseau bas niveau.
osInformations système : plateforme, mémoire disponible, cœurs CPU, dossier home.
cryptoChiffrement, hachage (SHA256, MD5…), génération d'UUID, HMAC.
sqlite3 / better-sqlite3Base de données locale SQLite — persistance structurée sans serveur. (via npm)
Tous les packages npmL'intégralité de l'écosystème npm (400 000+ packages) est accessible dans le Main Process.
APIs Electron natives
BrowserWindowCréer et gérer des fenêtres. Contrôle de la taille, position, décoration, opacité, frameless…
dialogBoîtes de dialogue natives : ouverture de fichier, dossier, sauvegarde, messages d'erreur/confirmation.
shellOuvrir des fichiers dans l'application par défaut, ouvrir des URLs dans le navigateur, afficher dans l'explorateur.
Menu / MenuItemMenus natifs de la barre d'application (macOS) et menus contextuels clic-droit.
TrayIcône dans la zone de notification système (systray Windows, barre de menu macOS).
NotificationNotifications système natives (centre de notifications Windows/macOS).
clipboardLire et écrire dans le presse-papiers (texte, HTML, images, formats personnalisés).
globalShortcutRaccourcis clavier globaux qui fonctionnent même quand la fenêtre n'a pas le focus.
powerMonitorDétecter la mise en veille, le réveil, l'état de la batterie, le changement de source d'alimentation.
screenInformations sur les écrans connectés, résolutions, densité de pixels, multi-moniteur.
autoUpdaterMise à jour automatique de l'application via Squirrel (Windows/macOS).
nativeImageManipulation d'images natives pour icônes, miniatures, captures d'écran.
Communication IPC
Le renderer ne peut pas accéder directement au système. Il passe par un preload script
qui expose des fonctions sécurisées via contextBridge.
const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('api', { // Renderer → Main ouvrirFichier: () => ipcRenderer.invoke('dialog:open'), lireFichier: (p) => ipcRenderer.invoke('fs:read', p), // Main → Renderer (événements) onProgress: (cb) => ipcRenderer.on('progress', (_, v) => cb(v)) })
const { ipcMain, dialog } = require('electron') const fs = require('fs') ipcMain.handle('dialog:open', async () => { const { filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] }) return filePaths[0] }) ipcMain.handle('fs:read', async (_, path) => { return fs.promises.readFile(path, 'utf8') })
// window.api est exposé par le preload — pas d'accès Node.js direct const btn = document.getElementById('open') btn.addEventListener('click', async () => { const filePath = await window.api.ouvrirFichier() const content = await window.api.lireFichier(filePath) document.getElementById('content').textContent = content })
Sécurité & bonnes pratiques
| Option | Valeur recommandée | Raison |
|---|---|---|
contextIsolation | true (défaut) | Sépare le contexte JS du renderer et du preload. |
nodeIntegration | false (défaut) | Empêche le renderer d'accéder à Node.js directement. |
webSecurity | true (défaut) | Maintient la politique Same-Origin dans le renderer. |
allowRunningInsecureContent | false | Bloque les ressources HTTP dans une app HTTPS. |
preload | Chemin absolu | Seul moyen sécurisé d'exposer des APIs Node.js au renderer. |
nodeIntegration: true + contextIsolation: false
dans une app qui charge du contenu distant. Une XSS dans le renderer deviendrait une
exécution de code arbitraire avec accès Node.js complet sur l'OS.Compatibilité par plateforme
Poids & performance
Le principal reproche fait à Electron est sa taille et sa consommation mémoire. Chaque application embarque une copie complète de Chromium et Node.js.
| Métrique | Electron | Tauri | HTA |
|---|---|---|---|
| Taille installateur | ~80–250 Mo | ~3–10 Mo | ~0 Ko |
| RAM au démarrage | ~150–400 Mo | ~30–80 Mo | ~20–50 Mo |
| Temps de démarrage | 2–5 sec | < 1 sec | < 1 sec |
| Performances UI | Excellentes (V8) | Excellentes | Limitées (IE) |
| Partage Chromium | Non (par défaut) | WebView natif OS | MSHTML partagé |
asar (archive compressée), le tree-shaking webpack/vite, et
l'exclusion des dépendances dev. VS Code pèse ~100 Mo installé malgré sa richesse fonctionnelle.Packaging & distribution
# Installer electron-builder npm install --save-dev electron-builder # Package.json — configuration build { "build": { "appId": "com.monapp.desktop", "win": { "target": ["nsis", "portable"] }, "mac": { "target": "dmg", "category": "public.app-category.utilities" }, "linux": { "target": ["AppImage", "deb"] } } } # Générer les installateurs pour toutes les plateformes npx electron-builder --win --mac --linux
| Outil | Rôle | Format de sortie |
|---|---|---|
| electron-builder | Le plus complet, NSIS, dmg, AppImage, auto-update | .exe .dmg .AppImage .deb .rpm |
| Electron Forge | Officiel, intégré au CLI, plus simple | .exe .zip .deb .rpm |
| electron-packager | Basique, sans installeur | Dossier avec l'exe |
Cas d'usage appropriés
| Cas d'usage | Pertinence | Raison |
|---|---|---|
| Éditeur de code / IDE | EXCELLENT | VS Code en est la preuve. Accès fichiers + extensions npm. |
| Client de messagerie / collaboration | EXCELLENT | Slack, Discord — notifications, systray, mise à jour auto. |
| Outil desktop cross-platform distribué | TRÈS BON | Un seul codebase pour Win + Mac + Linux. |
| App web existante → desktop | TRÈS BON | Enveloppe une SPA React/Vue existante en quelques jours. |
| Utilitaire personnel léger | OVERKILL | 150 Mo pour un utilitaire simple : préférer HTA (Windows) ou Tauri. |
| Application mobile | IMPOSSIBLE | Desktop uniquement — utiliser React Native ou Flutter. |
| Application haute performance / jeu | DÉCONSEILLÉ | Overhead Chromium. Préférer Qt, Flutter, ou natif. |