How to implement a simple drag-and-drop using Create-React-App and react-beautiful-dnd?

Yiğit Atak
CodeX
Published in
9 min readApr 4, 2022

--

Today, I was working on a personal project and I had to implement a drag-and-drop feature to allow more customization within my application. I spent a lot of time on articles, official docs, and YouTube videos. After a lengthy process of studying and trying to implement it, I finally got it to work. But how? Well, before we begin, let’s create a React app!

npx create-react-app dnd-example
cd dnd-example
npm start

Congratulations! Now we have a React app.

This is only the beginning though. Let’s install the package we’ll use today.

npm i react-beautiful-dnd

If you don’t know what react-beautiful-dnd is, that’s completely fine. It’s a library developed by an amazing team at Atlassian, the company behind Jira. It’s also completely open-source and the project can be accessed here. Next, we need to wrap our app between a DragDropContext component. Remember, you can only have one DragDropContext so it’s a good idea to wrap your entire app between it, that’s also how the official docs suggest you use it. This is what my App.js file looks like at the moment.

DragDropContext is needed to use the drag and drop feature react-beautiful-dnd offers. The library we use does not support nested DragDropContexts. However, DragDropContext requires an onDragEnd callback. This callback is called once you’re done dragging the element. For simplicity’s sake, I’ll use a function. I won’t be giving out codes in a copy-pastable format since I think practice is important. Here’s how our app looks so far:

Let’s break it down one by one.

We defined a function, we called it handleOnDragEnd and it accepts a parameter, we called it result. The parameter isn’t important right now, we’ll get to it later. For our drag and drop feature to work, it needs to be wrapped between a DragDropContext component. It has a lot of available props and callbacks, but none of them is required except onDragEnd. So, we added our handler function as a prop to our DragDropContext component. Let’s add a list of red boxes to our website so we have something to drag and drop. For the HTML and CSS codes, I’ll go ahead and give you the code since this isn’t an HTML or a CSS tutorial. In our App.js file, we need the draggable content within our DragDropContext tags. So, I’ll go ahead and do this:

<ul><li><div className="box red"></div></li><li><div className="box green"></div></li></ul>

And in our App.css file, I’ll do this:

ul {list-style: none;padding-left: 5px;}.box {width: 200px;height: 50px;margin-bottom: 5px;}.red {background: red;}.green {background: green;}

I won’t go into detail on these. You’ll notice that we still can’t drag or drop our boxes. Let’s focus on dropping them first. For that, we need to import another component called Droppable. Droppable helps you define the area in which you’ll be able to drop the elements. For my current use case, I want the entirety of my ul element to be a droppable area. So let’s do that.

Okay, easy enough, right? Well, our Droppable component also needs a property called droppableId. You might be asking why. Well, your entire HTML page can have more than 1 droppable areas and react-beautiful-dnd needs to identify which one you’re dropping your elements on. Okay, let’s add an id. This can be anything as long as it’s a string. I’ll go ahead and call it “boxes.”

There are more props like type, isDropDisabled, and so on but this is going to be a simple drag and drop. You can check out their official docs here if you want to dig deeper into it. Now, our Droppable component also needs a function with two arguments. These arguments are; provided and snapshot. For this example, we’ll only use provided and that’s the only required parameter. We get 3 important things with provided; provided.innerRef provided.placeholder and provided.droppableProps. In short, you need to bind provided.innerRef to the highest possible DOM node. This helps you look up your DOM nodes without the use of ReactDOM. provided.placeholder creates a placeholder space when you’re dragging an element around. It’s important to put the placeholder within whichever component you bound provided.innerRef to. As you may have noticed, our last prop, provided.droppableProps, is a spread operator. It contains all the data and styling react-beautiful-dnd needs. Bear in mind that you need to put all these props in the same element. If you have an in-line styling, you need to type out these props first as provided.droppableProps also contains styling and it’ll overwrite your inline styling. Let’s put all these to practice:

If you check your website now, nothing will change. But why? Well, our elements still aren’t draggable! Let’s work on that now. First, we need to import Draggable. Draggable is similar to Droppable in many cases, it simply makes our elements draggable. You can have more than one Droppable and Draggable as opposed to DragDropContext. A Draggable always needs to be contained within a Droppable and a Droppable always needs to be contained within a DragDropContext. Similar to Droppable, Draggable also needs an ID since you can have more than one within the same document. However, before we move further, I want to refactor some of my code as you’ll most likely use this with an array and not hand-written divs. I’ll also explain the logic behind it so even if you use it with hand-written divs, you’ll be fine. If you’re following along, I’ll define a simple array of objects within our App component like so:

Pretty self-explanatory. Now, let’s refactor the way we display our divs:

This article assumes you at least have basic knowledge of React so I won’t go into detail on what map does. I destructured our array and assigned a key value to our list item. For the class, I used what is called a template literal. You can read my article here if you don’t know what that is. If you refresh the page, absolutely nothing will change. Let’s get to adding the drag feature to our website now. First, I need to wrap the content I want to be draggable between our Draggable component. In my case, this is our li element.

Draggable requires an index and an ID. The ID needs to be a string once again. We’ll use the index we’re getting from our map function. Well, the problem is our ID is a number… and that’s exactly why we’ll use the good old toString() function. Let’s do it!

Okay, now we’re getting somewhere. Similar to Droppable, our Draggable component also needs a function with two arguments. These arguments are our good old friends provided and snapshot. Again, we’ll only use provided and it’s the only required argument. Similar to Droppable, we get 3 important things; provided.innerRef, provided.draggableProps, provided.dragHandleProps. Once again, these need to be bound to the same element. What they do is similar to Droppable. We’ll add these to the list element since we want it to be draggable.

However, there’ll be a little tweak. We’ll move our key prop to our Draggable component. Each draggable element requires a key.

Okay, amazing! Now if you refresh the page you should be able to drag and drop your elements around, wooo!

Okay, that’s great but this is pointless. It doesn’t even save when I drag and drop elements. Well, for that, we’ll use states. Let’s define a state. First, import useState.

Next, we define a state. I’ll call it boxes, I’ll also get rid of our array at the beginning and just assign it to our state.

Okay, if you refresh the page… absolutely nothing will change! That’s because we aren’t handling what happens when dragging ends. That’s why we defined handleonDragEnd, right? Well, let’s get to that! I’ll start this off with simply console.log’ing what result does.

Okay, so it’s an object that has a bunch of fancy things we can use but we’re only interested in destination and source at the moment. Source has information on which element we dragged and destination has information on, you guessed it, where we dragged it on to. With this knowledge, I’ll create a new array from our box array.

Great. Next, I want to remove whichever element was dragged from my new array and add it to wherever it was moved. For this, I need to use the indexes of source and destination like so:

Okay, but that doesn’t mean anything on its own, right? We are simply creating a new array. Well, we have to set our state to our new array like so:

Now, let’s check our website again!

Well, it works! But there’s a little bug. What if I drag and drop our boxes onto somewhere our Droppable isn’t defined? Let’s try that:

Well, as expected, my box goes back to its original position and I get this error. The solution is simple. You simply return if the destination is null like so:

Now you have a flawlessly working simple drag and drop feature using react-beautiful-dnd by Atlassian! I’ll add a GitHub repo link down below for people to inspect the code I’ve written for this article.

As always, please leave a comment down below if you have any improvements over this code or if you have a question. If you’d like to see more articles from my daily learning routine, consider following me as well. With that being said, I’ll see you all in my next article!

Github Repo

--

--

Yiğit Atak
CodeX

Hacettepe University Computer Education and Instructional Technology & Computer Science student. Learning MERN stack web development.