Curious case of Drag and Drop
Ankur Kedia
Posted on January 30, 2024
Introduction
Drag and drop may appear as a simple user interaction where you pick an item up and place it elsewhere, similar to organizing items on a Trello board or any Kanban-style interface where cards or information can be effortlessly rearranged with a click and drag.
However, this whole process hides a lot of complexity behind it. Starting with moving data between different parts, and getting the correct drop position. This challenge escalates when you have a nested layer of elements that can move across multiple levels.
To implement this, we don’t have to reinvent the wheel here, we have several libraries at our disposal. The most popular ones are:
react-beautiful-dnd stands out as the most commonly used, it provides a clean and high-level API with a lot of abstraction. It was developed by Atlassian.
react-dnd is quite powerful but a bit complex and requires some getting used to.
dnd-kit is one of the newest ones, it is modern, light-weight, and performant.
Problem
We, at epilot have relied on react-beautiful-dnd
extensively and in different parts of our app.
However, we hit some roadblocks in certain contexts while trying some complex scenarios where it was not accurately predicting the drop location of the element. Here is one example.
We devised several hacks for specific edge cases but they fell short of solving all of the issues. The code to move element data based on drop position was turning into Spaghetti code because of those edge cases. The discontinuation of maintenance and support of react-beautiful-dnd
was also not helping its case to keep using it.
Solution
Finally, we decided to explore alternative libraries that could solve our problems with a more explicit, intuitive, and simpler API. After evaluating several options, we settled with dnd-kit
as it provided an explicit and simple API. An added benefit was that it also provides hooks API which is missing in some of the older libraries.
Key advantages of dnd-kit
include:
Zero dependencies
Optimized Performance
Accessibility
Support for Multiple Input Methods
Comprehensive documentation and examples
Show me some code
Here are the codes for a simple drag-and-drop with both libraries.
// react-beautiful-dnd
export const DragDrop = ({ initialData }) => {
const [items, setItems] = useState(initialData);
const onDragEnd = () => { /** moving data **/ };
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<ul {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<DraggableItem item={item} key={item.id} index={index} />
))}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
);
};
const DraggableItem = ({ item, index }) => (
<Draggable draggableId={item.id} index={index}>
{(provided) => (
<li
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
{item.content}
</li>
)}
</Draggable>
);
// dnd-kit
export const DndKit = ({ initialData }) => {
const [items, setItems] = useState(initialData);
const onDragEnd = () => { /** moving data **/ };
const sensors = useSensors(useSensor(PointerSensor));
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext items={items} strategy={verticalListSortingStrategy}>
<ul>
{items.map((item) => (
<DraggableItem item={item} key={item.id} />
))}
</ul>
</SortableContext>
</DndContext>
);
};
const DraggableItem = ({ item }) => {
const {
attributes,
isDragging,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({
id: item.id,
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
cursor: isDragging ? "grabbing" : "grab",
};
return (
<li
{...attributes}
{...listeners}
key={item.id}
data-dnd-id={item.id}
data-dnd-type="item"
ref={setNodeRef}
style={style}
>
{item.content}
</li>
);
};
Check out this Codesandbox for the complete code.
Among these code samples having the same functionality, you can tell that dnd-kit
has a higher complexity but is also more comprehensive. It uses Sortable to address this as it is one of the use cases it solves. It aligns with our objective to solve more intricate scenarios with nested drag and drop and the ability to drag across different levels.
Key advantages with dnd-kit
that came in handy include:
Custom Placeholder - It is one of the most desired use cases. Achieving this in react-beautiful-dnd can be challenging as it will have limited capabilities and a lot of JavaScript to attain a satisfactory state. In our scenario, we wanted to show a condensed version of the element with children during dragging, so we used
DragOverlay
with the React portal.Collision Detection Strategies - It offers various strategies specific to different use cases providing control over element switching within the tree. You can even develop your custom strategy to fit your requirements.
Sorting Strategies - Similarly, it offers different sorting strategies enabling sorting of vertical lists, horizontal lists, or grids.
Check out this simplified version of our use case at Epilot featuring drag and drop functionality with nested elements in this Codesandbox.
Conclusion
Ultimately, the choice between the two depends on your specific requirements, preferences, and the level of customization you want. If you prefer a simpler and more opinionated approach, react-beautiful-dnd would be a better choice. If you are looking for more customization and control, dnd-kit might be fit your needs better.
Posted on January 30, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.