Skip to content

Instantly share code, notes, and snippets.

@xmedeko
Last active July 30, 2025 13:57
Show Gist options
  • Save xmedeko/81d63d56b96c2bb23d59b4f28861a88f to your computer and use it in GitHub Desktop.
Save xmedeko/81d63d56b96c2bb23d59b4f28861a88f to your computer and use it in GitHub Desktop.
electron-open-file-dialog-slow-bug
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>showOpenDialog is slow</title>
</head>
<body>
<h1>showOpenDialog is slow</h1>
<p>On Windows, <code>dialog.showOpenDialog</code> is slow so, before it become modal, browser windows receives input events.</p>
<p>Bug has not been reported, but similar bug <a href="https://github.com/electron/electron/issues/35562">#35562 [Bug]: Programmatic focus doesn't respect modal dialog box]</a> aleady exists.</p>
<div>
<p>Click a button and press some key (e.g. Escape) or press a key first and then click a button.</p>
<br>
<button id="openFileBtn">Open File</button> &nbsp;
<button id="okMessageBtn">OK Message Dialog</button> &nbsp;
<button id="errorBtn">Error Dialog</button> &nbsp;
<button id="noOpBtn">No Op</button> &nbsp;
<br/>
<br/>
<input id="msgInput" readonly />
<div>
<script src="./renderer.js"></script>
</body>
</html>
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
},
});
// and load the index.html of the app.
mainWindow.loadFile('index.html');
// Open the DevTools.
mainWindow.webContents.openDevTools();
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow();
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit();
})
ipcMain.handle('showOpenDialog', async (event) => {
return dialog.showOpenDialog(BrowserWindow.fromWebContents(event.sender), {
properties: ["openFile"],
filters: [
{ name: 'All Files', extensions: ['*'] }
]
});
});
ipcMain.handle('showOkMessageBox', async (event, message) => {
return dialog.showMessageBox(BrowserWindow.fromWebContents(event.sender), {
type: 'info',
message,
buttons: ['OK']
});
});
ipcMain.handle('showErrorBox', async (event, title, content) => {
return dialog.showErrorBox(title, content)
});
ipcMain.handle('noOp', async (event) => { });
{
"name": "open-file-dialog-slow-bug",
"productName": "open-file-dialog-slow-bug",
"description": "My Electron application description",
"keywords": [],
"main": "./main.js",
"version": "1.0.0",
"author": "xmedeko",
"scripts": {
"start": "electron ."
},
"dependencies": {},
"devDependencies": {
"electron": "37.2.4"
}
}
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("MainProcessAPI", {
showOpenDialog() { return ipcRenderer.invoke('showOpenDialog'); },
showOkMessageBox(message) { return ipcRenderer.invoke('showOkMessageBox', message); },
showErrorBox(title, content) { return ipcRenderer.invoke('showErrorBox', title, content); },
noOp() { return ipcRenderer.invoke('noOp'); }
});
const body = document.getElementsByTagName('body')[0];
const openFileBtn = document.getElementById('openFileBtn');
const okMessageBtn = document.getElementById('okMessageBtn');
const errorBtn = document.getElementById('errorBtn');
const noOpBtn = document.getElementById('noOpBtn');
const msgInput = document.getElementById('msgInput');
let inCall = false;
let keyPressed = 0;
body.addEventListener('keydown', async (evt) => {
if (!inCall)
return;
// Try Escape, F1
keyPressed++;
console.log(`Key ${evt.key} pressed: #${keyPressed}`);
msgInput.value = `${evt.key} pressed #${keyPressed}`;
});
openFileBtn.addEventListener('click', async () => {
const result = await doCall('showOpenDialog', () => MainProcessAPI.showOpenDialog());
if (keyPressed || result.canceled)
return;
msgInput.value = result.filePaths[0];
});
okMessageBtn.addEventListener('click', async () => {
await doCall('showOkMessageBox', () => MainProcessAPI.showOkMessageBox('Some message.'));
if (keyPressed)
return;
msgInput.value = 'OK';
});
errorBtn.addEventListener('click', async () => {
await doCall('errorBtn', () => MainProcessAPI.showErrorBox('Test error', 'Some error message.'));
if (keyPressed)
return;
msgInput.value = 'Error closed';
});
noOpBtn.addEventListener('click', async () => {
await doCall('noOp', () => MainProcessAPI.noOp());
if (keyPressed)
return;
msgInput.value = 'No op finished.';
});
async function doCall(opName, fn) {
keyPressed = 0;
msgInput.value = '';
inCall = true;
console.log(opName + ' before call');
const result = await fn();
console.log(opName + ' after call');
inCall = false;
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment