npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

electron-draggable

v1.6.2

Published

Multi-platform Electron window drag tool.

Downloads

716

Readme

Installation

Install the electron-draggable package via npm:

npm install electron-draggable

Overview

The electron-draggable package provides a lightweight, main-process solution for enabling window dragging in frameless Electron windows. It hooks into the webContents.on('before-mouse-event') to intercept mouse events and move the window accordingly, without requiring any CSS (-webkit-app-region) or renderer-process code.

Highlights

  • Zero renderer-side code — everything runs in the main process.
  • Works with BrowserWindow and BaseWindow — supports both standard and multi-view setups.
  • Selector-based filtering — include or exclude specific elements from dragging via CSS selectors.
  • Drag region — restrict dragging to a specific region (e.g., a custom title bar) using partial bounds.
  • Double-click to maximize — optional built-in support for maximize/unmaximize on double-click.
  • Configurable FPS — drag update rate defaults to the screen refresh rate.

Usage

All public methods are documented with JSDoc and can be referenced during import.

Importing

To import the Draggable class:

const { Draggable } = require('electron-draggable')

Creating an Instance

Using Draggable.from (recommended)

The from static method returns a single cached instance per window. If an instance already exists for the window, it is returned immediately:

const drag = Draggable.from(window)

You can also pass drag options during creation:

const drag = Draggable.from(window, { region: { height: 40 }, maximize: true })

When a BrowserWindow is used, its webContents is automatically attached as a drag source (unless attachOnInit is set to false).

Using Draggable.create

Use create when you need multiple independent drag managers for the same window:

const drag = Draggable.create(window, options)

Each instance independently manages its own drag configuration and WebContents attachments. The instance is not cached on the window object.

Drag Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | region | Partial<Rectangle> | Entire Window | Restricts dragging to a region. Only specified bounds are checked (e.g., { height: 40 } for the top 40px). | | selector | string | Disabled | CSS selector that marks elements as drag handles. Can be combined with exclude. | | exclude | string | Disabled | CSS selector for elements that should NOT trigger drag. Can be combined with selector. | | button | 'left' | 'right' | 'middle' | 'left' | Mouse button that triggers the drag. | | maximize | boolean | Disabled | Enable double-click to maximize/unmaximize. | | fps | number | Screen refresh rate | Frames per second for drag position updates. | | attachOnInit | boolean | Enabled | If true, auto-attach the BrowserWindow.webContents on initialization. |

Note: selector and exclude can be used together. Use selector to whitelist draggable areas, exclude to blacklist non-draggable elements, or both to whitelist a region and carve out exceptions within it.

Button

The button option specifies which mouse button initiates the drag. Accepted values are 'left', 'right', and 'middle'.

// Drag with right-click (useful when left-click is reserved for other interactions)
Draggable.from(window, { button: 'right' })

// Drag with middle-click
Draggable.from(window, { button: 'middle' })

Region

The region option defines draggable bounds using up to 4 independent thresholds. Only specified attributes are checked — omitted ones impose no constraint:

| Attribute | Constraint | Default | |-----------|-----------|---------| | x | Drag only when cursor is at or after x px from the left | 0 (no left bound) | | y | Drag only when cursor is at or after y px from the top | 0 (no top bound) | | width | Drag only within width px after x | (no right bound) | | height | Drag only within height px after y | (no bottom bound) |

        x     x + width
        ┬        ┬
 ┌──────┼────────┼──────┐
 │ 0,0  │        │      │
 │      │        │      │
 │      ░░░░░░░░░░──────┼─┤ y
 │      ░░ DRAG ░░      │
 │      ░░ ZONE ░░      │
 │      ░░░░░░░░░░──────┼─┤ y + height
 │                      │
 │                      │
 └──────────────────────┘

Examples:

// Title bar: top 40px
{ height: 40 }

// Sidebar handle: left 8px
{ width: 8 }

// Custom band: between 50px and 150px from top
{ y: 50, height: 100 }

// Scoped box: specific region
{ x: 100, y: 50, width: 300, height: 150 }

// Entire content
/* empty */

Attaching and Detaching WebContents

For BaseWindow setups with multiple WebContentsView children, you can manually attach and detach drag sources:

const drag = Draggable.from(baseWindow)

// Attach a view's webContents with per-view overrides
drag.attach(view.webContents, { exclude: '.no-drag, button' })

// Detach when no longer needed
drag.detach(view.webContents)

// Or detach all at once
drag.detachAll()

Each attached WebContents can have its own override options that are merged with the instance defaults.

Updating Options

You can update options at any time. Changes apply immediately to all subsequent drag interactions:

// Update options for ALL attached WebContents
drag.updateOptions({ fps: 120, maximize: true })

// Update options for a SPECIFIC WebContents
drag.updateOptions(view.webContents, { region: { height: 60 } })

Destroying Drag

To permanently disable drag and release all references:

drag.destroy()

After calling destroy, the instance is dead and must not be reused. The drag is also automatically disabled when the window is closed.

Retargeting a Window

If you need to move the drag instance to a new window:

drag.setWindow(newWindow)

This clears the old window reference and sets the new one, preserving all attached WebContents and their configurations.

Quick Examples

BrowserWindow (simplest setup)

const { app, BrowserWindow } = require('electron')
const { Draggable } = require('electron-draggable')

app.whenReady().then(() => {
  const window = new BrowserWindow({ frame: false })
  window.loadURL('https://github.com/ECRomaneli/electron-draggable')

  // Enable dragging on the top 40px, with double-click to maximize
  Draggable.from(window, { region: { height: 40 }, maximize: true })
})

BaseWindow with WebContentsView

const { app, BaseWindow, WebContentsView } = require('electron')
const { Draggable } = require('electron-draggable')

app.whenReady().then(() => {
  const window = new BaseWindow({ width: 800, height: 600, frame: false })

  const view = new WebContentsView()
  window.contentView.addChildView(view)
  view.setBounds({ x: 0, y: 0, width: 800, height: 600 })
  view.webContents.loadFile('index.html')

  // Create drag instance and attach the view
  const drag = Draggable.from(window, { region: { height: 100 } })
  drag.attach(view.webContents, { exclude: '.no-drag, button' })
})

How It Works

  1. The Draggable instance hooks into webContents.on('before-mouse-event') for each attached WebContents.
  2. On mouseDown, it checks whether the click position is within the drag region and matches the selector/exclude rules (using document.elementFromPoint via executeJavaScript).
  3. If the position is draggable, it records the offset between the cursor and the window position.
  4. On mouseMove, it starts an interval (at the configured FPS) that reads the cursor screen position and updates the window position accordingly.
  5. On mouseUp (or any non-move event), the interval is cleared and dragging stops.
  6. If maximize is enabled and a double-click is detected, the window is toggled between maximized and normal states.

API Reference

Static Methods

/**
 * Get or create a Draggable instance for the given window.
 * Returns a single cached instance per window.
 * @param {BaseWindow} window - The Electron window to enable dragging for.
 * @param {DragOptions} [options] - Optional drag behavior configuration.
 * @returns {Draggable} A unique Draggable instance for this window.
 */
Draggable.from(window, options)

/**
 * Create a new independent Draggable instance for the given window.
 * The instance is NOT cached on the window object.
 * @param {BaseWindow} window - The Electron window to enable dragging for.
 * @param {DragOptions} [options] - Optional drag behavior configuration.
 * @returns {Draggable} A new independent Draggable instance.
 */
Draggable.create(window, options)

Instance Methods

/**
 * Register a WebContents as a drag source for this window.
 * @param {WebContents} webContents - The WebContents to attach.
 * @param {DragOptions} [overrideOptions] - Optional per-WebContents drag options.
 */
attach(webContents, overrideOptions)

/**
 * Unregister a WebContents from dragging.
 * @param {WebContents} webContents - The WebContents to detach.
 */
detach(webContents)

/**
 * Unregister all WebContents from dragging.
 */
detachAll()

/**
 * Disable drag for all registered WebContents and release the window reference.
 * After calling this method, the instance is dead and must not be reused.
 */
destroy()

/**
 * Update drag options for all registered WebContents.
 * @param {Partial<DragOptions>} update - Partial options to merge.
 */
updateOptions(update)

/**
 * Update drag options for a specific WebContents.
 * @param {WebContents} webContents - The WebContents to update options for.
 * @param {Partial<DragOptions>} update - Partial options to merge.
 */
updateOptions(webContents, update)

/**
 * Retarget the instance to a new window.
 * @param {BaseWindow} newWindow - The new window to attach to.
 */
setWindow(newWindow)

Author

Created by Emerson Capuchi Romaneli (@ECRomaneli).

License

This project is licensed under the MIT License.