Recommended way of making React component/div draggable

Learn recommended way of making react component/div draggable with practical examples, diagrams, and best practices. Covers javascript, reactjs development techniques with visual explanations.

Making React Components Draggable: A Comprehensive Guide

Hero image for Recommended way of making React component/div draggable

Learn the best practices and various approaches to implement draggable functionality for React components and HTML elements, from simple CSS to advanced libraries.

Creating draggable elements is a common requirement in modern web applications, enhancing user experience for tasks like reordering lists, positioning widgets, or interacting with dashboards. In React, there are several ways to achieve this, ranging from using native browser APIs to leveraging specialized libraries. This article will explore the recommended approaches, providing practical examples and insights into their implementation.

Understanding Draggable Concepts

Before diving into implementation, it's crucial to understand the core concepts behind draggable functionality. At its heart, dragging involves tracking mouse or touch events (mousedown/touchstart, mousemove/touchmove, mouseup/touchend) to update an element's position. The key is to calculate the displacement from the initial click/touch point and apply that as a transform or position change to the element.

flowchart TD
    A[User Clicks/Touches Element] --> B{Event Listener: onMouseDown/onTouchStart}
    B --> C[Record Initial Position (x, y) and Element Offset]
    C --> D{Event Listener: onMouseMove/onTouchMove}
    D --> E[Calculate New Position based on Mouse/Touch Delta]
    E --> F[Update Element's Style (e.g., transform: translate() or position: absolute)]
    F --> D
    D --> G{Event Listener: onMouseUp/onTouchEnd}
    G --> H[Stop Tracking, Clean Up Listeners]
    H --> I[Element is in New Position]

Basic Draggable Logic Flow

Method 1: Native HTML Drag and Drop API

The native HTML Drag and Drop API provides built-in browser support for dragging elements. While powerful for transferring data between elements (like dragging a file into a drop zone), it can be a bit cumbersome for simply moving an element around the screen. It's best suited when you need to transfer data or interact with drop targets.

import React from 'react';

const DraggableNative = () => {
  const handleDragStart = (e) => {
    e.dataTransfer.setData('text/plain', e.target.id);
    e.dataTransfer.effectAllowed = 'move';
    // Optional: Add a class for visual feedback during drag
    e.target.classList.add('dragging');
  };

  const handleDragEnd = (e) => {
    // Optional: Remove the class
    e.target.classList.remove('dragging');
  };

  return (
    <div
      id="my-draggable-div"
      draggable="true"
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
      style={{
        width: '100px',
        height: '100px',
        backgroundColor: 'lightblue',
        border: '1px solid blue',
        cursor: 'grab',
        position: 'relative' // Important for positioning later if dropped
      }}
    >
      Drag Me (Native)
    </div>
  );
};

export default DraggableNative;

Implementing a basic draggable element using native HTML Drag and Drop API.

Method 2: Custom Event-Based Draggable Component

For more control and a smoother user experience, especially when you just want to move an element visually, implementing a custom draggable component using mouse/touch events is a popular and flexible approach. This method gives you full control over the dragging logic and styling.

import React, { useState, useRef, useEffect } from 'react';

const DraggableCustom = ({ children }) => {
  const [isDragging, setIsDragging] = useState(false);
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const offset = useRef({ x: 0, y: 0 });
  const draggableRef = useRef(null);

  const handleMouseDown = (e) => {
    setIsDragging(true);
    offset.current = {
      x: e.clientX - position.x,
      y: e.clientY - position.y,
    };
  };

  const handleMouseMove = (e) => {
    if (!isDragging) return;
    setPosition({
      x: e.clientX - offset.current.x,
      y: e.clientY - offset.current.y,
    });
  };

  const handleMouseUp = () => {
    setIsDragging(false);
  };

  useEffect(() => {
    const currentRef = draggableRef.current;
    if (currentRef) {
      currentRef.addEventListener('mousedown', handleMouseDown);
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
    }

    return () => {
      if (currentRef) {
        currentRef.removeEventListener('mousedown', handleMouseDown);
      }
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isDragging, position]); // Re-run if dragging state or position changes

  return (
    <div
      ref={draggableRef}
      style={{
        position: 'absolute',
        left: position.x,
        top: position.y,
        cursor: isDragging ? 'grabbing' : 'grab',
        userSelect: 'none',
        zIndex: isDragging ? 1000 : 'auto',
        padding: '10px',
        backgroundColor: 'lightcoral',
        border: '1px solid red',
      }}
    >
      {children || 'Drag Me (Custom)'}
    </div>
  );
};

export default DraggableCustom;

A custom React component for draggable functionality using mouse events.

Method 3: Using a Third-Party Library (e.g., react-draggable)

For robust, feature-rich draggable functionality with less boilerplate, third-party libraries are often the best choice. Libraries like react-draggable handle many complexities, including boundary constraints, snapping, and performance optimizations. This is often the recommended approach for production applications.

npm install react-draggable

Installation command for react-draggable.

import React from 'react';
import Draggable from 'react-draggable';

const DraggableLibrary = () => {
  return (
    <Draggable
      axis="both"
      handle=".handle"
      defaultPosition={{x: 0, y: 0}}
      position={null}
      grid={[25, 25]}
      scale={1}
      onStart={() => console.log('Drag started')}
      onDrag={() => console.log('Dragging')}
      onStop={() => console.log('Drag stopped')}
    >
      <div
        style={{
          width: '150px',
          height: '150px',
          backgroundColor: 'lightgreen',
          border: '1px solid green',
          padding: '10px',
          cursor: 'grab',
        }}
      >
        <div className="handle" style={{ backgroundColor: 'darkgreen', color: 'white', padding: '5px', marginBottom: '5px', cursor: 'move' }}>
          Drag Handle
        </div>
        Drag Me (Library)
      </div>
    </Draggable>
  );
};

export default DraggableLibrary;

Example of a draggable component using the react-draggable library.

Choosing the Right Approach

The best method depends on your specific needs:

  • Native HTML Drag and Drop: Use when you need to transfer data between elements or interact with specific drop zones, especially for file uploads or reordering items in a list where the browser's default behavior is sufficient.
  • Custom Event-Based: Ideal when you need fine-grained control over the dragging behavior, performance, and styling, and when the native API's data transfer features are not required. It's a good learning exercise and suitable for simpler visual drags.
  • Third-Party Libraries (e.g., react-draggable): Recommended for most production applications. They provide a robust, tested, and feature-rich solution, handling edge cases and offering advanced functionalities like grid snapping, boundaries, and performance optimizations out-of-the-box. This saves development time and reduces potential bugs.