Pour créer une application bureau à partir d'une application Capacitor, enveloppez la même build web.webDir) dans une coquille Electron : un processus principal qui ouvre un BrowserWindow, un script de préchargement qui expose une petite application bureau API, et electron-builder pour produire des installateurs pour macOS, Windows et Linux. Votre interface utilisateur et votre logique métier restent partagées avec iOS et Android, et les fonctionnalités uniquement bureau vivent dans le processus principal Electron.
Cette guide couvre l'état actuel de Capacitor sur Electron en 2026, un setup sécurisé qui fonctionne avec Capacitor 8, le comportement des plugins, la mise en boîte et la signature, et comment envoyer des mises à jour web sans nouveaux installateurs.
L'état de Capacitor sur Electron en 2026
Pendant des années, la réponse par défaut était @capacitor-community/electron, qui a branché Electron sur le Capacitor CLI en tant que plateforme. Ce projet est maintenant marqué comme non maintenu dans son fichier README. Son dernier npm de version 5.0.1, a été livré en septembre 2023, nécessite Capacitor 5.4 ou ultérieur, et a été construit contre Electron 26. Electron a livré de nombreuses versions majeures depuis, et seuls les trois derniers majors reçoivent des correctifs de sécurité.
Vous pouvez toujours l'utiliser sur des projets anciens, mais pour un Capacitor 8, la comparaison ressemble à ceci :
| Approche | Avantages | Cons |
|---|---|---|
| Votre propre coquille Electron (ce guide) | Contrôl’Electron complet, sans abstraction supplémentaire | Vous écrivez environ 60 lignes de main et de préchargement code |
@capacitor-community/electron |
Connu cap commandes |
Non maintenu, bloqué sur une ancienne Capacitor et Electron |
L'approche de shell est moins fastidieuse qu'elle ne le semble, car Capacitor produit déjà une mise en page web statique. Electron n'a besoin que de la charger.
Le comportement des plugins dans Electron
Le rendu de Electron est Chromium, donc votre application Capacitor s'exécute là comme elle le ferait dans un navigateur. Le noyau de Capacitor détecte pas de pont natif et recule sur chaque implémentation web du plugin.
| Type de plugin | Fonctionne-t-il dans Electron? | Qu'est-ce à faire |
|---|---|---|
| Plugin avec une implémentation web (Préférences, Partage, Presse-papier, nombreux plugins Capgo) | Oui, en utilisant la version web | Tester le comportement web |
| Plugin natif uniquement (pas d'implémentation web) | No, rejette comme non implémenté | Écrivez un équivalent Electron derrière un préchargement API |
| Bibliothèques JS pures | Oui | Rien |
Capacitor.getPlatform() retourne 'web' dans ce setup et Capacitor.isNativePlatform() retourne false. Nous exposons un drapeau de bureau explicite depuis le script de préchargement afin que votre code puisse brancher proprement.
Prerequisites
- Une application Capacitor dont
bun run buildproduit un dossier statique (lewebDirincapacitor.config.ts, souventdistouwww). - Bun et une version LTS Node.js actuelle, car les outils Electron fonctionnent toujours sur Node.
- Pour la distribution macOS, un certificat Apple Developer ID. Pour Windows, un certificat de signature code.
Étape 1 : Installez Electron et le packager
bun add -d electron electron-builder
Ajoutez un electron/ dossier au niveau du projet :
my-app/
capacitor.config.ts
dist/ # web build, shared with iOS and Android
electron/
main.ts
preload.ts
electron-builder.yml
Étape 2 : Faites charger la construction web depuis le disque
Electron charge les fichiers avec le file:// protocole en production. Deux paramètres sont importants :
- Chemins d'asset relatifs. With Vite, configurez
base: './'pour que les fichiersindex.htmlsoient référencés./assets/...au lieu de/assets/.... - la navigation. Les liens profonds comme
/settingsUtilisez la navigation client côté pour le build bureau, ou assurez-vous que votre routeur ne navigue que côté client après chargement.index.html.
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
base: './',
});
Les chemins relatifs fonctionnent également sur iOS et Android, vous n'avez donc pas besoin d'une build séparée.
Étape 3 : Écrivez le processus principal
// electron/main.ts
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import path from 'node:path';
const isDev = !app.isPackaged;
async function createWindow() {
const win = new BrowserWindow({
width: 1280,
height: 800,
minWidth: 800,
minHeight: 600,
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
},
});
win.once('ready-to-show', () => win.show());
// Open external links in the default browser, never inside the app.
// Only allow https: so a script cannot launch file: or other protocol handlers.
win.webContents.setWindowOpenHandler(({ url }) => {
try {
if (new URL(url).protocol === 'https:') void shell.openExternal(url).catch(() => {});
} catch {
// invalid URL: ignore
}
return { action: 'deny' };
});
if (isDev) {
await win.loadURL('http://localhost:5173');
win.webContents.openDevTools({ mode: 'detach' });
} else {
await win.loadFile(path.join(__dirname, '..', '..', 'dist', 'index.html'));
}
}
ipcMain.handle('desktop:get-version', () => app.getVersion());
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
Keep contextIsolation: true, nodeIntegration: falseConservez sandbox: trueVotre application web peut charger des scripts tiers, et aucun d'entre eux ne doit accéder à Node.js.
Étape 4 : Exposez un petit bureau API depuis la préchargement
// electron/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('desktop', {
isElectron: true,
platform: process.platform,
getVersion: () => ipcRenderer.invoke('desktop:get-version') as Promise<string>,
});
Typez-le dans votre code web et utilisez-le de la même manière que vous utiliseriez un plugin :
// src/desktop.ts
declare global {
interface Window {
desktop?: {
isElectron: boolean;
platform: string;
getVersion: () => Promise<string>;
};
}
}
export const isDesktop = () => Boolean(window.desktop?.isElectron);
Chaque fonctionnalité native uniquement nécessaire sur le bureau devient une ipcMain.handle une dans le processus principal et une fonction dans la préchargement. Validez les arguments dans le processus principal, car le rendu est non fiable.
Étape 5 : Construire et exécuter
Compilez les fichiers Electron avec Bun. La préchargement s'exécute en sandbox, il faut donc la bundler en un seul fichier CommonJS :
{
"main": "electron/dist/main.js",
"scripts": {
"build": "vite build",
"electron:compile": "bun build electron/main.ts --outdir electron/dist --target node --format cjs --external electron && bun build electron/preload.ts --outdir electron/dist --target node --format cjs --external electron",
"electron:dev": "bun run electron:compile && electron .",
"electron:pack": "bun run build && bun run electron:compile && electron-builder"
}
}
Durant le développement, démarrez votre serveur de développement dans un terminal (bun run dev) et bun run electron:dev en un autre. Vous obtenez un redémarrage chaud dans la fenêtre du bureau.
Étape 6 : Installer des packages avec electron-builder
# electron-builder.yml
appId: com.example.myapp
productName: My App
directories:
output: release
files:
- dist/**/*
- electron/dist/**/*
- package.json
mac:
target: [dmg, zip]
category: public.app-category.productivity
hardenedRuntime: true
win:
target: [nsis]
linux:
target: [AppImage, deb]
category: Utility
Exécutez bun run electron:packelectron-builder construit pour le système d'exploitation sur lequel vous l'exécutez. Utilisez une matrice de lanceurs macOS, Windows et Linux dans CI pour produire les trois.
| OS | Cibles courantes | Signature |
|---|---|---|
| macOS | .dmg, .zip |
Certificat de développeur ID plus notarisation, ou Gatekeeper bloque l'application |
| Windows | NSIS .exe, MSI |
Code certificat de signature pour éviter les avertissements de SmartScreen |
| Linux | AppImage, .deb, .rpm |
Facultatif |
Les applications Electron sont plus grandes que les builds mobiles car elles incluent Chromium et Node.js. Attendez-vous à des installateurs d'environ 80 à 150 MB en fonction des cibles.
Étape 7 : Envoyez des mises à jour web sans nouveaux installateurs
La plupart des modifications dans une application Capacitor sont des changements web. Vous pouvez les déployer sur mobile. les mises à jour Capgo en direct. Sur le bureau, @capgo/electron-updater Faites la même chose avec le même modèle : les canaux, un bundle intégré, les bundles téléchargés, et le retrait automatique si le nouveau bundle ne fonctionne pas. notifyAppReady().
bun add @capgo/electron-updater
Mettez à jour le processus principal pour permettre à l'actualiseur de choisir le bundle à charger :
// electron/main.ts (production branch)
import { ElectronUpdater, setupIPCHandlers, setupEventForwarding } from '@capgo/electron-updater';
const updater = new ElectronUpdater({
appId: 'com.example.myapp',
autoUpdate: true,
});
// inside createWindow(), instead of loading dist/index.html directly
const builtinPath = path.join(__dirname, '..', '..', 'dist', 'index.html');
await updater.initialize(win, builtinPath);
setupIPCHandlers(updater);
setupEventForwarding(updater, win);
await win.loadFile(updater.getCurrentBundlePath());
Ajoutez le pont de l'actualiseur au même fichier de préchargement :
// electron/preload.ts (next to the desktop API)
import { exposeUpdaterAPI } from '@capgo/electron-updater/preload';
exposeUpdaterAPI();
Et confirmez un lancement sain depuis l'application web, uniquement sur le bureau :
import { isDesktop } from './desktop';
if (isDesktop()) {
const { requireUpdater } = await import('@capgo/electron-updater/renderer');
await requireUpdater().notifyAppReady();
}
Téléchargez un nouveau bundle avec le même CLI que vous utilisez pour mobile :
bun run build
bunx @capgo/cli@latest bundle upload --channel=production
Les mises à jour web ne peuvent pas modifier Electron lui-même, le processus principal ou le préchargement. Pour ceux-ci, envoyez un nouveau installateur avec un actualiseur auto-actualiseur. Notre guide sur les mises à jour Electron couvre cette trajectoire, et le page du plugin electron-updater et docs couvre toutes les options.
Troubleshooting
fenêtre blanche dans l'application embarquée. Les assets utilisent des chemins absolus. Définissez base: './' and rebuild. Open DevTools in the packaged app to confirm 404s on file:///assets/....
Les routes affichent une page blanche après rechargement. Le routeur a essayé de charger /route À partir du disque. Passer à la navigation par hash pour le bureau.
require is not defined ou les erreurs de chargement préalable. Le préchargement sandboxé ne peut pas charger des packages arbitraires en temps de exécution. Emballez-le dans un seul fichier, comme dans le script de compilation ci-dessus.
Une appelle de plugin rejette avec "non implémenté sur le web". Cette plugin n'a pas d'implémentation web. Ajoutez un gestionnaire d'IPC dans le processus principal et appelez-l’à travers window.desktop.
macOS dit que l'application est endommagée ou provient d'un développeur non identifié. L'application n'est pas signée et notarisée. Configurez un certificat de développeur ID et la notarisation dans electron-builder.
Updates roll back right after install. notifyAppReady() n'a pas été appelé dans le délai (10 secondes par défaut). Appelez-le tôt dans le démarrage de votre application.
Rappel
Vous n'avez pas besoin d'un package de plateforme dédié Capacitor pour atteindre le bureau. Un processus principal Electron court, un chargement préalable qui expose un API typé, des chemins d'asset relatifs et electron-builder vous donnent des installateurs pour les trois systèmes d'exploitation à partir d'une même build web. Ajoutez @capgo/electron-updater et l'application bureau obtient le même workflow live update que vos builds iOS et Android. Si vous choisissez toujours un runtime bureau, lisez notre Comparaison Electron vs Tauri.