New to KendoReactStart a free 30-day trial

Getting Started with the Draggable
Premium

The useDraggable hook and Draggable component enable the user to drag a native HTML Element or custom React Component.

The draggable functionality provides the following callbacks which enable the developer to implement custom positioning, styling, or behavior based on the current application state and the event arguments:

  • onPress—A callback indicating that the draggable item has been pressed, but not dragged yet.
  • onDragStart—A callback indicating that the draggable item has been dragged for the first time.
  • onDrag—A callback called on every pointer/touch action, indicating that a drag is happening.
  • onDragEnd—A callback indicating that the draggable item is no longer being dragged.
  • onRelease—A callback indicating that the draggable item has been released.

Basic Usage

The following example demonstrates the draggable functionality in action.

Change Theme
Theme
Loading ...

Registering for Drag

To register an element/component to the useDraggable hook, pass it as the first parameter. For the Draggable component, it is enough to provide a single children prop to the component.

In the following section, you will learn how to:

  • Pass a native HTML element to the useDraggable hook or the Draggable component.
  • Pass a custom React component to the useDraggable hook or the Draggable component.

Native HTML Element

  • To pass a native HTML Element to the useDraggable hook, obtain the ref object by using the React.useRef hook.

You can use the same ref object for other parts of your application, as long as you do not mutate it directly.

jsx
const element = React.useRef();
// ...
useDraggable(element, {});
// ...
return <div ref={element}>Drag Me</div>;
  • To pass a native HTML Element to the Draggable component, pass the desired element as a direct child.
jsx
    render() {
        return (
            <Draggable>
                <div>Drag Me</div>
            </Draggable>
        )
    }

The Draggable component will obtain the ref of the child component. If you require a reference, use the childRef.

jsx
    element = React.createRef();
    // ...
    componentDidMount() {
        // Sample code
        element.current.focus();
    }
    // ...
    render() {
        return (
            <Draggable childRef={this.element}>
                <div>Drag Me</div>
            </Draggable>
        )
    }

Custom React Component

There are two possible ways to pass a custom React Component to the useDraggable hook:

  • If the Component is a Functional Component:

    1. To obtain the ref, wrap the component in React.forwardRef.

      jsx
      const CustomComponent = React.forwardRef((props, ref) => {
          return <div ref={ref}>DragMe</div>;
      });

      Alternatively, if you are already using the ref and applying additional properties, add the element field to the ref, which will resolve to the HTML element underneath.

      jsx
      const CustomComponent = React.forwardRef((props, ref) => {
          const element = React.useRef();
      
          React.useImperativeHandle(ref, () => ({
              element: element.current
          }));
      
          return <div ref={element}>DragMe</div>;
      });
    2. Obtain the ref of your CustomComponent and pass it to the useDraggable hook.

      jsx
      const component = React.useRef();
      // ...
      useDraggable(component, {});
      // ...
      return <CustomComponent ref={component}>Drag Me</CustomComponent>;
  • If the Component is a Class Component:

    1. Provide a public element getter as a class field of the component.

      jsx
      class CustomComponent extends React.Component {
          _element = React.createRef();
      
          get element() {
              return this._element;
          }
      
          render() {
              return <div ref={this._element}>DragMe</div>;
          }
      }
    2. Wrap your CustomComponent into a Draggable.

      jsx
          render() {
              return (
                  <Draggable>
                      <CustomComponent>Drag Me</CustomComponent>
                  </Draggable>
              )
          }

Handling Drag Events

The KendoReact drag callbacks provide the necessary information to apply correct positioning on the page. The drag can be positioned relatively to its parent or absolutely in the document body.

Relative Positioning

To calculate the drag position correctly, you need:

  • The initial clientX/clientY—The client coordinates where the dragging begins.
  • The current clientX/clientY—The client coordinates of the current positioning of the pointer.
  • The offsetX/offsetY—The offset coordinates indicating where the pointer was positioned relative to the drag.
  • The scrollX/scrollY—The scroll coordinates required to account for the current scroll position of the parent.

To position the drag relative to its parent:

  1. Set the initial offsetX and offsetY values by handling the onPress callback.

    jsx
    const handlePress = (event) => {
        setOffset({ x: event.offsetX, y: event.offsetY });
    };
  2. Set the initial clientX and clientY values by handling the onDragStart callback.

    jsx
    const handleDragStart = (event) => {
        setInitial({ x: event.clientX - offset.x, y: event.clientY - offset.y });
    };
  3. Calculate the current position of the drag by handling the onDrag callback.

    jsx
    const handleDrag = (event) => {
        setTranslate({
            x: event.clientX - initial.x - offset.x + event.scrollX,
            y: event.clientY - initial.y - offset.y + event.scrollY
        });
    };
  4. Finally, apply the transform style to the element.

    jsx
    return (
        <div
            style={{
                transform: `translate(${translate.x}px, ${translate.y}px)`;
            }}
        >
            DragMe
        </div>
    )

Absolute Positioning

Absolute positioning is most-suitable for scenarios where the drag might happen between two different parent elements.

To calculate the drag position correctly, you need:

  • The initial clientX/clientY—The client coordinates where the dragging begins.
  • The current clientX/clientY—The client coordinates of the current positioning of the pointer.
  • The offsetX/offsetY—The offset coordinates indicating where the pointer was positioned relative to the drag.

Due to the drag being positioned absolutely within the body element, there is no need to account for the scroll coordinates of the parent.

To position the drag element absolutely:

  1. Set the initial offsetX and offsetY values by handling the onPress callback.

    jsx
    const handlePress = (event) => {
        setOffset({ x: event.offsetX, y: event.offsetY });
    };
  2. Set the initial clientX and clientY values by handling the onDragStart callback.

    jsx
    const handleDragStart = (event) => {
        setInitial({ x: event.clientX - offset.x, y: event.clientY - offset.y });
    };
  3. Calculate the current position of the drag by handling the onDrag callback.

    jsx
    const handleDrag = (event) => {
        setCoordinates({
            x: event.clientX - initial.x - offset.x,
            y: event.clientY - initial.y - offset.y
        });
    };
  4. Finally, apply the transform style to the element.

    jsx
    return ReactDOM.createPortal(
        <div
            style={{
                position: 'absolute',
                left: coordinates.x,
                top: coordinates.y
            }}
        >
            DragMe
        </div>,
        document.body
    );