{"id":11696,"library":"react-draggable","title":"React Draggable Component","description":"react-draggable is a React component library that makes HTML elements draggable within a web application. The current stable version is 4.5.0, with minor bugfix releases occurring periodically within a major version, and major versions released less frequently, indicating a stable and mature project. A key differentiator is that it doesn't create an additional wrapper element in the DOM, directly extending the wrapped element with event handlers and styles. It uses CSS Transforms for movement, allowing elements to be dragged regardless of their initial positioning and enabling movement between drags without issue. The library offers both a higher-order `Draggable` component for simple use cases and a lower-level `DraggableCore` for more fine-grained control over drag interactions, supporting both controlled and uncontrolled component patterns. It also ships with TypeScript types.","status":"active","version":"4.5.0","language":"javascript","source_language":"en","source_url":"https://github.com/react-grid-layout/react-draggable","tags":["javascript","react","draggable","react-component","typescript"],"install":[{"cmd":"npm install react-draggable","lang":"bash","label":"npm"},{"cmd":"yarn add react-draggable","lang":"bash","label":"yarn"},{"cmd":"pnpm add react-draggable","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency for React applications.","package":"react","optional":false},{"reason":"Peer dependency for rendering React components to the DOM.","package":"react-dom","optional":false}],"imports":[{"note":"Draggable is the default export for the primary component.","wrong":"const Draggable = require('react-draggable');","symbol":"Draggable","correct":"import Draggable from 'react-draggable';"},{"note":"DraggableCore is a named export providing a lower-level drag primitive.","wrong":"const DraggableCore = require('react-draggable').DraggableCore;","symbol":"DraggableCore","correct":"import { DraggableCore } from 'react-draggable';"},{"note":"Type imports for event objects and data passed to drag handlers.","symbol":"DraggableEvent, DraggableData","correct":"import type { DraggableEvent, DraggableData } from 'react-draggable';"}],"quickstart":{"code":"import React from 'react';\nimport ReactDOM from 'react-dom';\nimport Draggable, { DraggableCore } from 'react-draggable';\n\ninterface AppState {\n  activeDrags: number;\n  deltaPosition: { x: number; y: number; };\n  controlledPosition: { x: number; y: number; };\n}\n\nclass App extends React.Component<{}, AppState> {\n\n  state = {\n    activeDrags: 0,\n    deltaPosition: { x: 0, y: 0 },\n    controlledPosition: { x: -400, y: 200 }\n  };\n\n  handleDrag = (e: any, ui: any) => {\n    const { x, y } = this.state.deltaPosition;\n    this.setState({\n      deltaPosition: { x: x + ui.deltaX, y: y + ui.deltaY }\n    });\n  };\n\n  onStart = () => {\n    this.setState({ activeDrags: ++this.state.activeDrags });\n  };\n\n  onStop = () => {\n    this.setState({ activeDrags: --this.state.activeDrags });\n  };\n\n  // For controlled component\n  adjustXPos = (e: any) => {\n    e.preventDefault();\n    e.stopPropagation();\n    const { x, y } = this.state.controlledPosition;\n    this.setState({ controlledPosition: { x: x - 10, y } });\n  };\n\n  adjustYPos = (e: any) => {\n    e.preventDefault();\n    e.stopPropagation();\n    const { x, y } = this.state.controlledPosition;\n    this.setState({ controlledPosition: { x, y: y - 10 } });\n  };\n\n  onControlledDrag = (e: any, ui: any) => {\n    const { x, y } = this.state.controlledPosition;\n    this.setState({\n      controlledPosition: { x: x + ui.deltaX, y: y + ui.deltaY }\n    });\n  };\n\n  onControlledDragStop = (e: any, ui: any) => {\n    this.onStop();\n    this.onControlledDrag(e, ui);\n  };\n\n  render() {\n    const dragHandlers = { onStart: this.onStart, onStop: this.onStop };\n    const { deltaPosition, controlledPosition } = this.state;\n    return (\n      <div>\n        <h1>React Draggable Demo</h1>\n        <Draggable {...dragHandlers}>\n          <div className=\"box\">\n            <div>I can be dragged!</div>\n          </div>\n        </Draggable>\n        <Draggable axis=\"x\" {...dragHandlers}>\n          <div className=\"box\">\n            <div>Only horizontal dragging</div>\n          </div>\n        </Draggable>\n        <Draggable axis=\"y\" {...dragHandlers}>\n          <div className=\"box\">\n            <div>Only vertical dragging</div>\n          </div>\n        </Draggable>\n        <Draggable handle=\".handle\" {...dragHandlers}>\n          <div className=\"box\">\n            <div className=\"handle\">Drag from here</div>\n            <div>Clicking here won't move me</div>\n          </div>\n        </Draggable>\n        <Draggable cancel=\".no-drag\" {...dragHandlers}>\n          <div className=\"box\">\n            <div className=\"no-drag\">Clicking here won't move me</div>\n            <div>Drag from here</div>\n          </div>\n        </Draggable>\n        <Draggable grid={[25, 25]} {...dragHandlers}>\n          <div className=\"box\">\n            <div>I snap to a 25 x 25 grid</div>\n          </div>\n        </Draggable>\n        <Draggable scale={0.5} {...dragHandlers}>\n          <div className=\"box\">\n            <div>I am 50% scale</div>\n          </div>\n        </Draggable>\n        <Draggable\n          defaultPosition={{x: 0, y: 0}}\n          position={controlledPosition}\n          onDrag={this.onControlledDrag}\n          onStop={this.onControlledDragStop}\n        >\n          <div className=\"box\">\n            I am a controlled component.\n            <br />\n            My position is ({controlledPosition.x}, {controlledPosition.y})\n            <br />\n            <button onClick={this.adjustXPos}>Adjust x (-10)</button>\n            <button onClick={this.adjustYPos}>Adjust y (-10)</button>\n          </div>\n        </Draggable>\n\n        <h2>DraggableCore</h2>\n        <DraggableCore {...dragHandlers}>\n          <div className=\"box no-cursor\">\n            I'm a DraggableCore.<br />\n            I only fire events.<br />\n            My position is ({deltaPosition.x}, {deltaPosition.y})\n          </div>\n        </DraggableCore>\n      </div>\n    );\n  }\n}\n\n// Basic CSS for demonstration\nconst styleSheet = document.createElement('style');\nstyleSheet.innerHTML = `\n.box {\n  background: #eee;\n  border: 1px solid #999;\n  border-radius: 3px;\n  width: 180px;\n  height: 180px;\n  margin: 10px;\n  padding: 10px;\n  float: left;\n  cursor: grab;\n  font-family: monospace;\n  font-size: 14px;\n  text-align: center;\n}\n.box .handle {\n  cursor: grab;\n  background-color: #ccc;\n  padding: 5px;\n  margin-bottom: 5px;\n}\n.box .no-drag {\n  cursor: not-allowed;\n  background-color: #fdd;\n  padding: 5px;\n  margin-bottom: 5px;\n}\n.box.no-cursor {\n  cursor: default;\n}\n`;\ndocument.head.appendChild(styleSheet);\n\nReactDOM.render(<App />, document.getElementById('root'));","lang":"typescript","description":"This quickstart demonstrates various configurations of the Draggable component, including basic dragging, axis-locked dragging, handle/cancel selectors, grid snapping, scaling, and controlled positioning. It also shows a basic usage of DraggableCore."},"warnings":[{"fix":"Review the v2.0.0 changelog and the official documentation for updated `onStart`, `onDrag`, `onStop` callback signatures and how to manage component position using `position` (controlled) or `defaultPosition` (uncontrolled).","message":"Version 2.0.0 introduced significant breaking changes, specifically affecting event callbacks and the usage of `position` and `defaultPosition` props. Previous implementations will need to be updated to match the new API.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"To avoid overwriting existing transforms, wrap your target element in an intermediate `<span>` or `<div>` element, and apply Draggable to this new wrapper: `<Draggable><span>Your Element</span></Draggable>`.","message":"If the element you are wrapping with Draggable already has CSS Transforms applied, Draggable will overwrite them. This is because Draggable uses CSS Transforms for its positioning.","severity":"gotcha","affected_versions":">=0.10"},{"fix":"Test your existing Draggable implementations, especially those relying on `handle` or `cancel` props, after upgrading to v2.1.0 or newer. Remove any workarounds you might have implemented for the previous incorrect behavior.","message":"Version 2.1.0 fixed an issue where `handle` or `cancel` selectors might be improperly missed if the event originated from a child of the handle or cancel element. This change, while a fix, might alter behavior for existing workarounds.","severity":"breaking","affected_versions":">=2.1.0"},{"fix":"Ensure you are using `react-draggable@4.5.0` or newer, which includes a fix for this issue. You might also need to explicitly prevent default touch behaviors on the draggable element or its parent if the problem persists with older versions.","message":"Dragging on iDevices could cause the entire window to scroll instead of just the draggable element.","severity":"gotcha","affected_versions":"<4.5.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Upgrade `react-draggable` to version 2.0.2 or newer, as this version specifically addressed bugs related to `clientX` being undefined on certain touch platforms.","cause":"This typically occurs on some touch-enabled platforms due to issues with event object normalization in older versions.","error":"Cannot read properties of undefined (reading 'clientX') or cannot access clientX of undefined"},{"fix":"Update `react-draggable` to version 2.2.1 or newer. This version includes a bugfix for `getComputedStyle` errors.","cause":"An invalid argument was passed to `window.getComputedStyle`, often related to how SVG elements or non-standard DOM nodes were being handled.","error":"Error: Argument 1 of Window.getComputedStyle does not implement interface Element."},{"fix":"Upgrade `react-draggable` to version 2.0.1 or newer. This version contains a specific fix for the IE10 constructor bug.","cause":"An incompatibility with JavaScript constructors in Internet Explorer 10 was causing errors during component initialization.","error":"Object doesn't support property or method 'constructor' in IE10"}],"ecosystem":"npm"}