Comparative Analysis of Popular React Drag-and-Drop Sorting Libraries
This article examines several popular libraries for implementing drag-and-drop functionality in React applications, focusing on their core concepts, usage, and compatibility with other UI components.
Core Libraries Overview
Four prominent libraries are discussed:
-
React DnD
- Core Concepts:
- Backend: Handles browser differences, DOM events, and translates them into internal Redux actions. Supports HTML5 backend and custom implementations.
- Item: A data object representing the dragged element (e.g.,
{ cardId: 42 }). - Type: Enumerated constants defining draggable item types within the application.
- Monitor: Stores and provides access to the drag-and-drop state (e.g., whether a drag is in progress, current item and type).
- Connector: Links components to the Backend, enabling access to the DOM.
- DragSource: A Higher-Order Component (HOC) making a component a draggable source.
- DropTarget: An HOC making a component a drop target.
- DragDropContext: Wraps the root component to provide the drag-and-drop context.
- Demo: codesandbox.io/s/github/re…
- Core Concepts:
-
React Beautiful DnD
- Core Concepts:
- DragDropContext: Wraps the root component. Both
DraggableandDroppablemust be descendants. - Draggable: Wraps components to make them draggable.
- Droppable: Wraps components to make them drop targets.
- DragDropContext: Wraps the root component. Both
- Demo: codesandbox.io/s/k260nyxq9v
- Core Concepts:
-
dnd-kit
- Core Concepts:
- DndContext: Wraps the root component, similar to
DragDropContext. - Droppable: Makes components capable of receiving dropped items.
- Draggable: Makes components capable of being dragged.
- Sensors: Detect various input methods (pointer, mouse, touch, keyboard) to initiate, track, and end drag operations.
- Modifiers: Dynamically alter sensor-detected coordinates. Use cases include constraining movement to an axis, within container boundaries, or applying friction.
- DndContext: Wraps the root component, similar to
- Demo: chromatic.com/iframe.html…
- Core Concepts:
-
react-sortable-hoc
- Core Concepts:
- SortableContainer: Creates a draggable sorting container.
- SortableElement: Creates a draggable sorting item.
- Demo: codesandbox.io/s/react-sortable-hoc-starter-o104x95y86
- Core Concepts:
Ant Design Table Integration
Examples demonstrating integration with Ant Design's Table component:
- React DnD: codesandbox.io/s/tuo-zhuai…
- react-sortable-hoc: codesandbox.io/s/tuo-zhuai…
- React Beautiful DnD: codesandbox.io/s/react-bea…
- dnd-kit: The provided demo stackblitz.com/edit/react-… was non-functional. An issue on
dnd-kit's GitHub github.com/clauderic/d… suggests potential conflicts with Ant Design's Table enhancements.
Tree Component Compatibility
- Ant Design's Built-in Tree Dragging:
- Demo: codepen.io/huxinmin/em…
- Limitations: Lacks real-time visual feedback during dragging; position updates only occur after dropping. Requires significant style customization.
- A tree can be conceptually treated as nested lists.
- Library Demos for Tree Dragging:
- React DnD: codesandbox.io/s/crazy-hoo…
- react-sortable-hoc: codesandbox.io/embed/react…
- React Beautiful DnD: codesandbox.io/embed/react…
- dnd-kit: codesandbox.io/embed/react…
Mobile Device Compatibility
- React DnD: codesandbox.io/embed/react…
- React Beautiful DnD: codesandbox.io/s/react-bea…
- react-sortable-hoc: codesandbox.io/s/tuo-zhuai…
- dnd-kit: chromatic.com/iframe.html…
Infinite Scrolling Suppport
Implementing infinite scrolling with drag-and-drop typically involves virtual list techniques. Library support varies:
- React DnD: codesandbox.io/embed/react…. Performance can be optimized using
requestAnimationFrame. Integration with other virtual list libraries is possible. - dnd-kit: chromatic.com/iframe.html…. This demo uses
react-tiny-virtual-list. - react-sortable-hoc: clauderic.github.io/react-sorta…. Integrates with
react-virtualized. - react-beautiful-dnd: react-beautiful-dnd.netlify.app/iframe.html…. Uses
react-virtualized.
Feature Comparision Summary
- React DnD
- Documentation: Comprehensive.
- GitHub Stars: 16.4k.
- Activity: Actively maintained (updated within the last month).
- Learning Curve: High.
- Features: Moderate.
- Mobile Support: Good.
- Examples: Moderate.
- Complexity: High due to numerous concepts and complex usage.
- Architecture: Promotes component decoupling.
- React Beautiful DnD
- Documentation: Comprehensive.
- GitHub Stars: 24.8k.
- Activity: Actively maintained (updated within the last three months).
- Learning Curve: High.
- Ease of Use: Moderate.
- Features: Rich.
- Mobile Support: Excellent.
- Examples: Abundant.
- Focus: Specifically designed for vertical and horizontal lists, offering a higher-level abstraction than React DnD but with less broad functionality.
- User Experience: Visually appealing, accessible, and provides a natural, physical feel to object movement.
- Philosophy: Primarily supports drag-and-drop, not copy/clone operations.
- dnd-kit
- Documentation: Comprehensive.
- GitHub Stars: 2.8k.
- Activity: Actively maintained (updated within the last month).
- Learning Curve: Moderate.
- Ease of Use: Moderate.
- Features: Moderate.
- Mobile Support: Moderate.
- Examples: Abundant.
- Copy/Clone: Not explicitly observed.
- react-sortable-hoc
- Documentation: Limited.
- GitHub Stars: 9.5k.
- Activity: Actively maintained (updated within the last three months).
- Learning Curve: Low.
- Ease of Use: Low.
- Features: Simple, primarily focused on sorting.
- Mobile Support: Moderate.
- Examples: Moderate.
- Cross-Container Dragging: Not supported.
- Copy/Clone: Not explicitly observed.
Recommendations:
- For integrating with Ant Design's
Table,react-sortable-hocoffers the simplest approach. - For infinite scrolling, while
react-sortable-hochas many examples,react-beautiful-dndmight be a better choice due to more extensive source code usage. - For tree dragging with basic requirements, Ant Design's built-in tree component suffices. For advanced needs,
react-beautiful-dndis recommended. - For mobile compatibility, consider
react-sortable-hocorreact-beautiful-dnd.
Implementing a Basic Drag-and-Drop Component
HTML5 Drag and Drop API
To make an element draggable, set the draggable attribute:
<img draggable="true">
Key event handlers:
ondrag: Fires while dragging.ondragend/ondragstart: Fires at the end and beginning of a drag operation.ondragover: Fires when a dragged element is over a valid drop target.ondragenter/ondragleave: Fires when a dragged element enters or leaves a drop target.ondrop: Fires when the element is dropped on a target.
Data transfer is managed via event.dataTransfer:
setData(type, data): Adds data to the transfer.typeis a custom string identifier,datais the payload.getData(type): Retrieves data by its type.clearData(): Clears all transferred data.setDragImage(element, x, y): Customizes the drag preview image.effectAllowed: Defines the allowed effects (e.g.,copy,move,link).
Functional and Architectural Design
- Utilize React Hooks.
- Components:
Drag(draggable item),Drop(drop target),DndContext(context provider). - Support for mobile devices.
- Support for sorting.
Code Implementation
Drag Component Usage:
<Drag index={1} id='1'>
<div>Draggable Content</div>
</Drag>
Drag Component Implemantation:
import { FC } from "react";
interface DragProps {
index: number;
id: string | number;
}
const Drag: FC<DragProps> = (props) => {
const handleDragStart = (ev) => {
// Transfer data
ev.dataTransfer.setData("index", props.index.toString());
ev.dataTransfer.setData("id", props.id.toString());
};
return (
<div draggable onDragStart={handleDragStart}>
{props.children}
</div>
);
};
export default Drag;
Drop Component Usage:
<Drop>
<Drag index={1} id='1'>
<div>Draggable Content</div>
</Drag>
</Drop>
Drop Component Implementation:
import { FC, useContext } from "react";
import { Context } from "./DndContext";
const Drop: FC = (props) => {
const { handleDragOver, handleDrop } = useContext(Context);
const dragOverHandler = (ev) => {
ev.preventDefault(); // Necessary to allow dropping
if (handleDragOver) handleDragOver();
};
const dropHandler = (ev) => {
// Retrieve data
const oldIndexStr = ev.dataTransfer.getData("index");
const oldIndex = parseInt(oldIndexStr, 10);
// Basic calculation for new index based on clientY.
// This is a simplification; actual implementation might be more complex.
const targetRect = ev.currentTarget.getBoundingClientRect();
const mouseYRelativeToTarget = ev.clientY - targetRect.top;
const itemHeight = 20; // Assume a fixed height for simplicity
const newIndex = Math.floor(mouseYRelativeToTarget / itemHeight);
if (!isNaN(oldIndex)) {
if (handleDrop) handleDrop(oldIndex, newIndex);
}
};
return (
<div onDragOver={dragOverHandler} onDrop={dropHandler}>
{props.children}
</div>
);
};
export default Drop;
DndContext Component Usage:
<DndContext
onDropEnd={(oldIndex, newIndex) => {
setData(arrayMove(data, oldIndex, newIndex));
}}
onDragOver={() => { /* Handle drag over if needed */ }}
>
<Drop>
{data.map((item, index) => (
<Drag key={item.id} id={item.id} index={index}>
<div className="item">{item.text}</div>
</Drag>
))}
</Drop>
</DndContext>
DndContext Component Implementation:
import { createContext, FC, ReactNode } from "react";
interface DndContextProps {
onDropEnd: (oldIndex: number, newIndex: number) => void;
onDragOver: () => void;
children: ReactNode;
}
interface ContextValue {
handleDragOver: () => void;
handleDrop: (oldIndex: number, newIndex: number) => void;
}
export const Context = createContext<ContextValue>({} as ContextValue);
const DndContext: FC<DndContextProps> = (props) => {
return (
<Context.Provider
value={{
handleDragOver: props.onDragOver,
handleDrop: props.onDropEnd,
}}
>
{props.children}
</Context.Provider>
);
};
export default DndContext;
For mobile compatibility, libraries like react-touch-dnd (github.com/timruffles/…) can be used.
Areas for Improvement
- Accurate calculation of drop position.
- Real-time position updates during drag.
- Performance optimizations.
- Robust error handling.
- Drag-and-drop visual feedback styles.
- Directional dragging (X/Y axis).
- And other enhancements.
Live Demo
See the complete online example: codesandbox.io/embed/react…