Curious case of Drag and Drop
Tags: dragndrop, react, javascript, webdev
Published: 2024-01-30 08:18:33

## 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](https://github.com/atlassian/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](https://github.com/react-dnd/react-dnd) is quite powerful but a bit complex and requires some getting used to.

- [dnd-kit](https://github.com/clauderic/dnd-kit) is one of the newest ones, it is modern, light-weight, and performant.


## Problem

We, at [epilot](https://epilot.cloud/) 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.


![epilot workflows builder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7tqoma48gkge2wvxc14.gif)


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](https://master--5fc05e08a4a65d0021ae0bf2.chromatic.com/?path=/story/examples-tree-sortable--drop-indicator)



## Show me some code

Here are the codes for a simple drag-and-drop with both libraries.


```js
// 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>
);
```

```js
// 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](https://codesandbox.io/p/sandbox/drag-and-drop-m87zsf) 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](https://docs.dndkit.com/api-documentation/context-provider/collision-detection-algorithms) - 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](https://docs.dndkit.com/presets/sortable/sortable-context#strategy) - 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](https://codesandbox.io/p/sandbox/nested-drag-and-drop-with-dnd-kit-ckmq8d).

## 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.