Recommended way of making React component/div draggable
Categories:
Making React Components Draggable: A Comprehensive Guide

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.
position: absolute
and updating left
/top
or transform: translate(x, y)
is generally preferred over margin
or padding
for performance, as transform
changes don't trigger layout recalculations.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.
react-draggable
offer advanced features such as constraining movement to a parent, snapping to a grid, and providing callbacks for drag events, significantly simplifying complex draggable interactions.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.