Electron - Defining Shortcuts
Electron - Defining Global Keyboard Shortcuts
Section titled “Electron - Defining Global Keyboard Shortcuts”Keyboard shortcuts significantly enhance user experience by providing quick access to application features. Electron’s globalShortcut module allows you to register global keyboard shortcuts that trigger actions even when your application window does not have focus (though this behavior can be OS-dependent).
The globalShortcut Module
Section titled “The globalShortcut Module”Important points about globalShortcut:
- It should only be used from the main process.
- Shortcuts are defined using Accelerator strings (e.g.,
'CommandOrControl+O','Shift+Alt+F1').CommandOrControlautomatically maps toCommandon macOS andControlon Windows/Linux. - It’s crucial to unregister shortcuts when your application quits using
app.on('will-quit', () => { globalShortcut.unregisterAll(); })or by unregistering specific shortcuts to prevent them from lingering after the app closes.
If a global shortcut needs to trigger an action in a renderer process (e.g., update the UI), the main process handler for the shortcut should use webContents.send() to communicate with the relevant BrowserWindow.
Let’s adapt the previous file-opening example. We’ll register CommandOrControl+O to trigger the open file dialog.
Your main.js will register the shortcut and handle the action:
const { app, BrowserWindow, globalShortcut, dialog, ipcMain } = require('electron');const path = require('path');const fs = require('fs').promises;
let mainWindow;
function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } }); mainWindow.loadFile('index.html'); mainWindow.on('closed', () => { mainWindow = null; });}
async function openFileAndSendData() { if (!mainWindow) return; // Ensure window exists
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, { title: 'Open Text File via Shortcut', properties: ['openFile'], filters: [{ name: 'Text Files', extensions: ['txt', 'md'] }] });
if (canceled || filePaths.length === 0) { mainWindow.webContents.send('file-data', { error: 'File open canceled.' }); return; }
const filePath = filePaths[0]; try { const content = await fs.readFile(filePath, 'utf-8'); mainWindow.webContents.send('file-data', { filePath, content }); } catch (error) { console.error('Failed to read file:', error); dialog.showErrorBox('Error Reading File', `Could not read file: ${error.message}`); mainWindow.webContents.send('file-data', { error: `Error reading file: ${error.message}` }); }}
app.whenReady().then(() => { createWindow();
// Register the global shortcut const ret = globalShortcut.register('CommandOrControl+O', () => { console.log('CommandOrControl+O is pressed'); openFileAndSendData(); });
if (!ret) { console.error('Failed to register global shortcut CommandOrControl+O'); }
// Check if a shortcut is registered console.log('Is CommandOrControl+O registered?', globalShortcut.isRegistered('CommandOrControl+O'));});
app.on('will-quit', () => { // Unregister all shortcuts when the application is about to quit. globalShortcut.unregisterAll(); console.log('All global shortcuts unregistered.');});
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit();});
app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow();});
// Optional: Allow triggering via IPC as well for a buttonipcMain.handle('trigger-open-file', async () => { // This function is not directly called by the shortcut but can be used by UI elements // The shortcut calls openFileAndSendData directly. // For simplicity, we can reuse the logic if a button also needs this. // However, the current example's shortcut directly sends data to renderer. // This IPC handler would be for a button that wants the main process to initiate. // For this example, we'll focus on the shortcut path.});Your preload.js will expose a listener for the renderer:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', { onFileData: (callback) => ipcRenderer.on('file-data', (_event, data) => callback(data)), // To clean up listener if component unmounts (useful in frameworks) removeOnFileData: () => ipcRenderer.removeAllListeners('file-data')});And your index.html will display a message and listen for data:
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>Global Shortcuts</title> <style> body { font-family: sans-serif; margin: 20px; } #fileInfo { margin-bottom: 10px; font-style: italic; } #fileContent { white-space: pre-wrap; border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; min-height: 100px; } </style></head><body> <h1>Global Shortcut Demo</h1> <p>Press **CommandOrControl+O** to open a text file.</p> <div id="fileInfo">No file opened yet via shortcut.</div> <pre id="fileContent">File content will appear here.</pre>
<script> const fileInfoDiv = document.getElementById('fileInfo'); const fileContentPre = document.getElementById('fileContent');
window.electronAPI.onFileData((data) => { if (data.error) { fileInfoDiv.textContent = `Error: ${data.error}`; fileContentPre.textContent = ''; console.error('Received error from main:', data.error); } else if (data.filePath && data.content !== undefined) { fileInfoDiv.textContent = `Displaying: ${data.filePath}`; fileContentPre.textContent = data.content; } else { // Fallback for unexpected data structure fileInfoDiv.textContent = 'Received data, but format is unexpected.'; fileContentPre.textContent = JSON.stringify(data, null, 2); } });
// Optional: Clean up the listener when the window is about to be unloaded // window.addEventListener('beforeunload', () => { // window.electronAPI.removeOnFileData(); // }); </script></body></html>Run the app (electron .). Now, even if you click outside the app window (if OS allows), pressing Ctrl+O (or Cmd+O on macOS) should trigger the open file dialog. Once a file is selected, its content will be displayed in the app window.
Common Accelerator Modifiers: Command (macOS only), Control (often abbreviated Ctrl), Alt, Shift, Super (Windows key or Command key). Keys include A-Z, 0-9, F1-F24, punctuation like Plus, Space, Tab, arrow keys (Up, Down, Left, Right), MediaNextTrack, etc. Refer to the Electron Accelerator documentation for a full list.
It’s good practice to provide user-configurable shortcuts if your application has many actions, but for common operations like Open, Save, Copy, Paste, using standard system shortcuts is highly recommended.