How I Made an Electron Window Draggable and Kept Mouse Enter/Leave Detection
Source: Dev.to
Introduction
I wanted my app to be fully draggable while still changing its height and width on mouse enter and mouse leave. By default, making a window draggable ignores mouse events in the drag area, which breaks normal enter/leave detection.
Visual comparison
Mouse outside
Mouse inside
Window configuration
const mainWindow = new BrowserWindow({
// width, height, x, y, etc.
titleBarStyle: 'hidden',
backgroundMaterial: "acrylic",
visualEffectState: "active",
vibrancy: "popover", // macOS
frame: false, // frameless window
});
Making the window draggable
Add the following class to any area that should act as a drag region:
.dragAble {
-webkit-user-select: none;
-webkit-app-region: drag;
}
For elements that must remain interactive, use:
.no-drag {
-webkit-app-region: no-drag;
}
Handling mouse‑enter and mouse‑leave
Electron does not provide native mouse‑enter/leave events for the whole window, so we implement them ourselves.
1. Polling listener
A utility runs on an interval, checks the cursor position against the window bounds, and calls a callback with the result.
function listenToMouseMovement(callback) {
const id = setInterval(() => {
const cursor = screen.getCursorScreenPoint();
const bounds = esm.mainWindow.getBounds();
const inside =
cursor.x >= bounds.x &&
cursor.x = bounds.y &&
cursor.y {
if (esm.isMovingWindow) return; // ignore while dragging
if (inside) {
// Cancel any pending leave timeout
if (leaveTimeout) {
clearTimeout(leaveTimeout);
leaveTimeout = null;
}
if (esm.mouseWasOutsideWindow) {
// Debounce the enter event
if (!enterTimeout) {
enterTimeout = setTimeout(() => {
esm.mouseWasOutsideWindow = false;
mainWindow.webContents.send("onMouseIsInsideTheApp");
}, 120);
}
}
} else {
// Cancel any pending enter timeout
if (enterTimeout) {
clearTimeout(enterTimeout);
enterTimeout = null;
}
if (!esm.mouseWasOutsideWindow) {
// Debounce the leave event
if (!leaveTimeout) {
leaveTimeout = setTimeout(() => {
esm.mouseWasOutsideWindow = true;
mainWindow.webContents.send("onMouseIsOutsideTheApp");
}, 120);
}
}
}
});
3. Ignoring mouse checks while the window is being moved
We track the window’s moving state using Electron’s native events:
esm.mainWindow.on("will-move", () => {
esm.isMovingWindow = true;
});
esm.mainWindow.on("move", () => {
esm.isMovingWindow = true;
if (esm.moveEndTimeout) {
clearTimeout(esm.moveEndTimeout);
esm.moveEndTimeout = null;
}
});
esm.mainWindow.on("moved", () => {
if (esm.moveEndTimeout) clearTimeout(esm.moveEndTimeout);
esm.moveEndTimeout = setTimeout(() => {
esm.isMovingWindow = false;
esm.moveEndTimeout = null;
}, 200);
});
Result
The approach eliminates flickering and allows the window to be fully draggable while still reacting to mouse‑enter and mouse‑leave events. You can see the behavior in action in this tweet: (link omitted for brevity)
Thanks for reading! This is my first article, and more is on the way.

