Building cross-platform desktop applications with Electron including IPC, auto-updates, and security
# Electron Desktop App Development for Google Antigravity
Build cross-platform desktop apps with Electron using Google Antigravity's Gemini 3 engine. This guide covers main/renderer processes, IPC communication, auto-updates, and security patterns.
## Electron Main Process
```typescript
// src/main/index.ts
import { app, BrowserWindow, ipcMain, dialog, Menu, Tray } from 'electron';
import { autoUpdater } from 'electron-updater';
import path from 'path';
import Store from 'electron-store';
const store = new Store();
let mainWindow: BrowserWindow | null = null;
let tray: Tray | null = null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
},
titleBarStyle: 'hiddenInset',
show: false,
});
// Load the app
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
}
// Show when ready
mainWindow.once('ready-to-show', () => {
mainWindow?.show();
});
// Handle window close
mainWindow.on('closed', () => {
mainWindow = null;
});
// Create menu
createMenu();
// Create tray
createTray();
// Check for updates
autoUpdater.checkForUpdatesAndNotify();
}
function createMenu() {
const template: Electron.MenuItemConstructorOptions[] = [
{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{
label: 'Preferences',
accelerator: 'CmdOrCtrl+,',
click: () => mainWindow?.webContents.send('open-settings'),
},
{ type: 'separator' },
{ role: 'quit' },
],
},
{
label: 'File',
submenu: [
{
label: 'New',
accelerator: 'CmdOrCtrl+N',
click: () => mainWindow?.webContents.send('file-new'),
},
{
label: 'Open',
accelerator: 'CmdOrCtrl+O',
click: async () => {
const result = await dialog.showOpenDialog(mainWindow!, {
properties: ['openFile'],
filters: [{ name: 'Documents', extensions: ['json', 'txt'] }],
});
if (!result.canceled && result.filePaths.length > 0) {
mainWindow?.webContents.send('file-opened', result.filePaths[0]);
}
},
},
{
label: 'Save',
accelerator: 'CmdOrCtrl+S',
click: () => mainWindow?.webContents.send('file-save'),
},
],
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
{ role: 'selectAll' },
],
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
},
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
function createTray() {
tray = new Tray(path.join(__dirname, '../assets/tray-icon.png'));
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show App',
click: () => mainWindow?.show(),
},
{ type: 'separator' },
{
label: 'Quit',
click: () => app.quit(),
},
]);
tray.setToolTip('My Electron App');
tray.setContextMenu(contextMenu);
tray.on('click', () => {
mainWindow?.isVisible() ? mainWindow.hide() : mainWindow?.show();
});
}
// App lifecycle
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// IPC handlers
ipcMain.handle('get-store-value', (_, key: string) => {
return store.get(key);
});
ipcMain.handle('set-store-value', (_, key: string, value: any) => {
store.set(key, value);
});
ipcMain.handle('show-save-dialog', async (_, options) => {
return dialog.showSaveDialog(mainWindow!, options);
});
ipcMain.handle('read-file', async (_, filePath: string) => {
const fs = await import('fs/promises');
return fs.readFile(filePath, 'utf-8');
});
ipcMain.handle('write-file', async (_, filePath: string, content: string) => {
const fs = await import('fs/promises');
await fs.writeFile(filePath, content, 'utf-8');
});
// Auto updater events
autoUpdater.on('update-available', () => {
mainWindow?.webContents.send('update-available');
});
autoUpdater.on('update-downloaded', () => {
mainWindow?.webContents.send('update-downloaded');
});
ipcMain.on('install-update', () => {
autoUpdater.quitAndInstall();
});
```
## Preload Script
```typescript
// src/main/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
const electronAPI = {
// Store operations
getStoreValue: (key: string) => ipcRenderer.invoke('get-store-value', key),
setStoreValue: (key: string, value: any) =>
ipcRenderer.invoke('set-store-value', key, value),
// File operations
showSaveDialog: (options: Electron.SaveDialogOptions) =>
ipcRenderer.invoke('show-save-dialog', options),
readFile: (path: string) => ipcRenderer.invoke('read-file', path),
writeFile: (path: string, content: string) =>
ipcRenderer.invoke('write-file', path, content),
// Event listeners
onFileNew: (callback: () => void) =>
ipcRenderer.on('file-new', callback),
onFileOpened: (callback: (path: string) => void) =>
ipcRenderer.on('file-opened', (_, path) => callback(path)),
onFileSave: (callback: () => void) =>
ipcRenderer.on('file-save', callback),
onOpenSettings: (callback: () => void) =>
ipcRenderer.on('open-settings', callback),
// Updates
onUpdateAvailable: (callback: () => void) =>
ipcRenderer.on('update-available', callback),
onUpdateDownloaded: (callback: () => void) =>
ipcRenderer.on('update-downloaded', callback),
installUpdate: () => ipcRenderer.send('install-update'),
// Cleanup
removeAllListeners: (channel: string) =>
ipcRenderer.removeAllListeners(channel),
};
contextBridge.exposeInMainWorld('electronAPI', electronAPI);
// TypeScript types for renderer
export type ElectronAPI = typeof electronAPI;
```
## Renderer Process (React)
```typescript
// src/renderer/App.tsx
import { useEffect, useState, useCallback } from 'react';
declare global {
interface Window {
electronAPI: import('../main/preload').ElectronAPI;
}
}
export function App() {
const [content, setContent] = useState('');
const [filePath, setFilePath] = useState<string | null>(null);
const [updateAvailable, setUpdateAvailable] = useState(false);
useEffect(() => {
// Listen for file events
window.electronAPI.onFileNew(() => {
setContent('');
setFilePath(null);
});
window.electronAPI.onFileOpened(async (path) => {
const fileContent = await window.electronAPI.readFile(path);
setContent(fileContent);
setFilePath(path);
});
window.electronAPI.onFileSave(async () => {
if (filePath) {
await window.electronAPI.writeFile(filePath, content);
} else {
const result = await window.electronAPI.showSaveDialog({
filters: [{ name: 'Text', extensions: ['txt'] }],
});
if (!result.canceled && result.filePath) {
await window.electronAPI.writeFile(result.filePath, content);
setFilePath(result.filePath);
}
}
});
// Listen for updates
window.electronAPI.onUpdateAvailable(() => {
setUpdateAvailable(true);
});
window.electronAPI.onUpdateDownloaded(() => {
if (confirm('Update ready. Restart now?')) {
window.electronAPI.installUpdate();
}
});
return () => {
window.electronAPI.removeAllListeners('file-new');
window.electronAPI.removeAllListeners('file-opened');
window.electronAPI.removeAllListeners('file-save');
};
}, [content, filePath]);
return (
<div className="app">
<header>
<h1>{filePath ? filePath.split('/').pop() : 'Untitled'}</h1>
{updateAvailable && <span className="update-badge">Update available</span>}
</header>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="Start typing..."
/>
</div>
);
}
```
## Best Practices
Google Antigravity's Gemini 3 engine recommends these Electron patterns: Enable context isolation and sandbox mode. Use IPC for all main/renderer communication. Implement proper security with preload scripts. Add auto-updates for desktop apps. Handle window state and persistence properly.This Electron prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their electron implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Electron projects, consider mentioning your framework version, coding style, and any specific libraries you're using.