React Draggable Component

4.5.0 · active · verified Sun Apr 19

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.

Common errors

Warnings

Install

Imports

Quickstart

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.

import React from 'react';
import ReactDOM from 'react-dom';
import Draggable, { DraggableCore } from 'react-draggable';

interface AppState {
  activeDrags: number;
  deltaPosition: { x: number; y: number; };
  controlledPosition: { x: number; y: number; };
}

class App extends React.Component<{}, AppState> {

  state = {
    activeDrags: 0,
    deltaPosition: { x: 0, y: 0 },
    controlledPosition: { x: -400, y: 200 }
  };

  handleDrag = (e: any, ui: any) => {
    const { x, y } = this.state.deltaPosition;
    this.setState({
      deltaPosition: { x: x + ui.deltaX, y: y + ui.deltaY }
    });
  };

  onStart = () => {
    this.setState({ activeDrags: ++this.state.activeDrags });
  };

  onStop = () => {
    this.setState({ activeDrags: --this.state.activeDrags });
  };

  // For controlled component
  adjustXPos = (e: any) => {
    e.preventDefault();
    e.stopPropagation();
    const { x, y } = this.state.controlledPosition;
    this.setState({ controlledPosition: { x: x - 10, y } });
  };

  adjustYPos = (e: any) => {
    e.preventDefault();
    e.stopPropagation();
    const { x, y } = this.state.controlledPosition;
    this.setState({ controlledPosition: { x, y: y - 10 } });
  };

  onControlledDrag = (e: any, ui: any) => {
    const { x, y } = this.state.controlledPosition;
    this.setState({
      controlledPosition: { x: x + ui.deltaX, y: y + ui.deltaY }
    });
  };

  onControlledDragStop = (e: any, ui: any) => {
    this.onStop();
    this.onControlledDrag(e, ui);
  };

  render() {
    const dragHandlers = { onStart: this.onStart, onStop: this.onStop };
    const { deltaPosition, controlledPosition } = this.state;
    return (
      <div>
        <h1>React Draggable Demo</h1>
        <Draggable {...dragHandlers}>
          <div className="box">
            <div>I can be dragged!</div>
          </div>
        </Draggable>
        <Draggable axis="x" {...dragHandlers}>
          <div className="box">
            <div>Only horizontal dragging</div>
          </div>
        </Draggable>
        <Draggable axis="y" {...dragHandlers}>
          <div className="box">
            <div>Only vertical dragging</div>
          </div>
        </Draggable>
        <Draggable handle=".handle" {...dragHandlers}>
          <div className="box">
            <div className="handle">Drag from here</div>
            <div>Clicking here won't move me</div>
          </div>
        </Draggable>
        <Draggable cancel=".no-drag" {...dragHandlers}>
          <div className="box">
            <div className="no-drag">Clicking here won't move me</div>
            <div>Drag from here</div>
          </div>
        </Draggable>
        <Draggable grid={[25, 25]} {...dragHandlers}>
          <div className="box">
            <div>I snap to a 25 x 25 grid</div>
          </div>
        </Draggable>
        <Draggable scale={0.5} {...dragHandlers}>
          <div className="box">
            <div>I am 50% scale</div>
          </div>
        </Draggable>
        <Draggable
          defaultPosition={{x: 0, y: 0}}
          position={controlledPosition}
          onDrag={this.onControlledDrag}
          onStop={this.onControlledDragStop}
        >
          <div className="box">
            I am a controlled component.
            <br />
            My position is ({controlledPosition.x}, {controlledPosition.y})
            <br />
            <button onClick={this.adjustXPos}>Adjust x (-10)</button>
            <button onClick={this.adjustYPos}>Adjust y (-10)</button>
          </div>
        </Draggable>

        <h2>DraggableCore</h2>
        <DraggableCore {...dragHandlers}>
          <div className="box no-cursor">
            I'm a DraggableCore.<br />
            I only fire events.<br />
            My position is ({deltaPosition.x}, {deltaPosition.y})
          </div>
        </DraggableCore>
      </div>
    );
  }
}

// Basic CSS for demonstration
const styleSheet = document.createElement('style');
styleSheet.innerHTML = `
.box {
  background: #eee;
  border: 1px solid #999;
  border-radius: 3px;
  width: 180px;
  height: 180px;
  margin: 10px;
  padding: 10px;
  float: left;
  cursor: grab;
  font-family: monospace;
  font-size: 14px;
  text-align: center;
}
.box .handle {
  cursor: grab;
  background-color: #ccc;
  padding: 5px;
  margin-bottom: 5px;
}
.box .no-drag {
  cursor: not-allowed;
  background-color: #fdd;
  padding: 5px;
  margin-bottom: 5px;
}
.box.no-cursor {
  cursor: default;
}
`;
document.head.appendChild(styleSheet);

ReactDOM.render(<App />, document.getElementById('root'));

view raw JSON →