How to Add Drag and Drop in React with React Beautiful DnD

Drag and drop is a common and intuitive way for users to interact with elements on a page. We‘re used to dragging and dropping files on our computers, rearranging cards on a Kanban board, and reordering playlists. Implementing this functionality in web apps greatly improves their usability and makes them feel more dynamic.

Luckily, adding drag and drop to a React app doesn‘t have to be a massive undertaking thanks to libraries like React Beautiful DnD. In this step-by-step guide, I‘ll show you how to make a list of items draggable in a matter of minutes. By the end, you‘ll have a solid foundation to add similar interactions to your own React projects. Let‘s get started!

What is React Beautiful DnD?

React Beautiful DnD is a lightweight library that makes it easy to implement drag and drop interfaces in React. It was created by Atlassian, the company behind products like Jira and Trello that rely heavily on drag and drop.

Some key features of the library:

  • Aims to have a lightweight, no-frills API
  • Unopinionated styling so it‘s easy to make it look how you want
  • Accessibility baked in
  • Great performance, even with big lists

The main concepts to understand are:

  • Draggable: A component that can be dragged around
  • Droppable: An area that Draggables can be dropped into
  • DragDropContext: The common parent component that wraps all the Droppables and Draggables

With those components, you can model basically any drag and drop interaction. Time to try it out!

Setting Up a React App

We‘ll start with a basic list of robot names that we want to make draggable. Here‘s the starter code:

import React from ‘react‘;

const initialRobots = [
  { id: ‘robot-1‘, name: ‘R2-D2‘ },  
  { id: ‘robot-2‘, name: ‘C-3PO‘ },
  { id: ‘robot-3‘, name: ‘BB-8‘ },
  { id: ‘robot-4‘, name: ‘K-2SO‘ },
  { id: ‘robot-5‘, name: ‘L3-37‘ },
];

function App() {
  return (
    <div>

      <ul>
        {initialRobots.map(robot => (
          <li key={robot.id}>
            <div>{robot.name}</div>
          </li>  
        ))}
      </ul>
    </div>
  );
}

export default App;

Which renders a plain list like this:

Initial list of robots

Our goal is to make this list fully draggable using React Beautiful DnD. Here‘s how to do it:

Step 1: Install react-beautiful-dnd

First, add the library to the project:

npm install react-beautiful-dnd

Step 2: Wrap the list in a DragDropContext

Import the DragDropContext component at the top of the file:

import { DragDropContext } from ‘react-beautiful-dnd‘; 

Then wrap the entire <ul> with it:

function App() {
  return (
    <DragDropContext>

      <ul>
        {/* ... */} 
      </ul>
    </DragDropContext>  
  );
}

The DragDropContext needs to be the common parent of all the draggable elements. It uses React‘s Context API to share drag and drop state with all its descendants.

Step 3: Make the List Droppable

A Droppable represents an area that Draggables can be dropped into. Let‘s turn our <ul> into a droppable area.

First import Droppable:

import { DragDropContext, Droppable } from ‘react-beautiful-dnd‘;

Then wrap the <ul> with Droppable and give it a unique id:

<Droppable droppableId="robots">
  {provided => (
    <ul ref={provided.innerRef} {...provided.droppableProps}>
      {initialRobots.map(robot => (
        <li key={robot.id}>
          <div>{robot.name}</div>
        </li>
      ))} 
      {provided.placeholder}
    </ul>
  )}  
</Droppable>

Notice the function-as-child pattern used by Droppable. This gives us access to a provided object with a few essential props:

  • provided.innerRef: This needs to be set as the ref on the drop area
  • provided.droppableProps: An object with props that need to be spread onto the droppable area
  • provided.placeholder: A React element used to create space in the Droppable as needed when an item is dragged

At this point our list isn‘t actually draggable yet, but it‘s ready to accept draggable items.

Step 4: Make List Items Draggable

Finally, the fun part! Let‘s wrap each <li> in a Draggable component to make them draggable.

Import Draggable:

import { DragDropContext, Droppable, Draggable } from ‘react-beautiful-dnd‘;

Then update the map to use Draggable:

{initialRobots.map((robot, index) => (
  <Draggable key={robot.id} draggableId={robot.id} index={index}>
    {provided => (
      <li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
        <div>{robot.name}</div>
      </li>
    )}  
  </Draggable>
))}

Similar to Droppable, Draggable also uses the function-as-child pattern to inject a provided object.

Each Draggable needs:

  • a unique draggableId
  • an index to indicate its position in the droppable area

On the actual elements being dragged, we need to set:

  • ref to provided.innerRef
  • provided.draggableProps to be spread onto the draggable element
  • provided.dragHandleProps to be spread onto the drag handle element

With that, our list is now fully draggable!

Draggable list

However, if you refresh the page you‘ll notice the order goes back to the original. That‘s because we haven‘t updated our state to reflect the new order after dragging. Let‘s fix that.

Step 5: Reorder Items on Drag End

To persist the dragged order, we need to update our state when a drag ends. Thankfully, the DragDropContext component has an onDragEnd callback prop that we can use.

First, let‘s move our robot list into React state and add a function to update it:

import React, { useState } from ‘react‘;

// ...

function App() {
  const [robots, setRobots] = useState(initialRobots);

  return (
    <DragDropContext>
      {/* ... */}
    </DragDropContext>
  );  
}

Next, define an onDragEnd function that will update the robots state:

function App() {
  const [robots, setRobots] = useState(initialRobots);

  const onDragEnd = result => {
    const { source, destination } = result;

    // dropped outside the list
    if (!destination) {
      return;
    }

    const newRobots = Array.from(robots);
    const [movedRobot] = newRobots.splice(source.index, 1);
    newRobots.splice(destination.index, 0, movedRobot);

    setRobots(newRobots);
  };  

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      {/* ... */} 
    </DragDropContext>
  );
}

The onDragEnd callback receives a result object with several properties. The ones we care about are:

  • source: The location the draggable item started at (its index in the list)
  • destination: The location the draggable item ended up (its new index in the list)

We first check if destination is defined, and return early if it‘s not. This handles the case where an item is dragged outside of a valid droppable area and "snaps back" to its original position.

If there is a valid destination, we make a copy of the robots array, splice out the moved item, and insert it at the new index. Finally, we update our state with the new robot order.

And with that, our list will maintain its order even after a refresh!

Persistent order

Next Steps

We‘ve covered the basic usage of React Beautiful DnD, but there‘s a lot more you can do with it. A few ideas:

  • Customize the appearance of items while dragging with the snapshot argument
  • Make multiple, distinct lists droppable with unique droppableIds
  • Validate whether a Draggable can be dropped in a Droppable with the type prop
  • Add custom drag handles instead of making the entire item draggable

I encourage you to read the official docs and explore the examples to see what‘s possible.

Conclusion

In this post, we saw how to add drag and drop to a list in React with the React Beautiful DnD library. The key steps were:

  1. Install react-beautiful-dnd
  2. Wrap the drag and drop area with a DragDropContext
  3. Wrap the droppable area with a Droppable
  4. Wrap each draggable item with a Draggable
  5. Update state to reflect the new order in an onDragEnd callback

I hope this tutorial has shown you that implementing drag and drop doesn‘t have to be a daunting task. With a library like React Beautiful DnD, you can add this engaging interaction to your app with ease. Now go forth and make all the lists draggable!

Similar Posts