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 aroundDroppable
: An area thatDraggable
s can be dropped intoDragDropContext
: The common parent component that wraps all theDroppable
s andDraggable
s
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:
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 Draggable
s 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 theref
on the drop areaprovided.droppableProps
: An object with props that need to be spread onto the droppable areaprovided.placeholder
: A React element used to create space in theDroppable
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
toprovided.innerRef
provided.draggableProps
to be spread onto the draggable elementprovided.dragHandleProps
to be spread onto the drag handle element
With that, our list is now fully draggable!
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!
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
droppableId
s - Validate whether a
Draggable
can be dropped in aDroppable
with thetype
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:
- Install
react-beautiful-dnd
- Wrap the drag and drop area with a
DragDropContext
- Wrap the droppable area with a
Droppable
- Wrap each draggable item with a
Draggable
- 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!