Drag and Drop Kanban board from scratch with React 🔥
Tags: webdev, react, javascript, typescript
Published: 2023-12-25 18:42:27

We all used those [Kanban](https://en.wikipedia.org/wiki/Kanban_(development))-style todo or task management apps where there are columns for each stages of a tasks and we can drag and drop tasks from one column to another. 

Implementing this drag and drop feature in react is way easier than I thought. Everything you need is already there without using any library or package. So let's get started. 🚀

*** ( Typescript is used but its not noticable in code examples JS users can easily understand and the CSS styles are omited in the code examples. Get to the [github link](https://github.com/NasifAhmed/dnd-article) to see the full code. )

## The idea 💡

The main idea behind it is pretty simple. We are going to use default HTML Drag and Drop API that uses various drag and drop events. To make any HTML element draggable we just need to add draggable attribute to an element. Then we will maintain a state where will store data for all the dropped elements and then show them in the div. To achieve this will use various drag and drop even listeners already available with HTML5 and React.

🎯 The main trick is here is that we can not move an element/component literally. We will pass its data with it when we drag and then recreate the element/component again from the passed data inside the div we are dropping it in.

We will use events listeners like :

1. [OnDrop](https://www.w3schools.com/jsref/event_ondrop.asp) : This event activates when a valid draggable element is dropped in its area.
2. [OnDragStart](https://www.w3schools.com/jsref/event_ondragstart.asp) : This is fired when dragging of an element is started.
3. [OnDragEnd](https://www.w3schools.com/jsref/event_ondragend.asp) : This is fired when dragging is ended.
4. [OnDragOver](https://www.w3schools.com/jsref/event_ondragover.asp) : This is fired when we drag over the area of an element

There are many more but for our use these are enough. So lets start the implementation.

## The implementation 🧑‍💻

Let's create some task elements in a div from where we will drag them from :


![The task divs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xvzj237bq52d24cd2r6.png)

and a todo div where we will drop them : 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w2xciaohy50s770c0ak8.png)

Now to make the task elements draggable we just add the draggable attribute.

```tsx
<div
    className="..."
    draggable
>
    Task 1
</div>
```

Now we can drag the task div. When we drag an element we need to pass its data with it so that we can recreate it. In this we case will pass its name.

To do this lets use `onDragStart` which fires when we start dragging an element.

```tsx
<div
    className="..."
    draggable
    onDragStart={(e) => {
        handleOnDrag(e, "Task 1");
    }}
>
    Task 1
</div>
```

Here we are passing the event e and the name of the task to the `handleOnDrag` function.

Lets write the `handleOnDrag` function

```ts
function handleOnDrag(e: React.DragEvent, name: string) {
    e.dataTransfer.setData("name", name);
}
```

Here we are setting the name string as data under `"name"` key. Now whenever we drag it the name data will go along with it.

We handled the dragging part now lets drop it.

We need a state where we will store data for all the dropped tasks and later show them in the div where we dropped them.

```tsx
const [tasks, setTasks] = useState<string[]>()
```

Now we will use `onDrop` attribute to signal the dropping event. But before doing this we have to handle `onDragOver` for an edge case. Just do this :

```tsx
<div 
    className="..."
    onDragOver={handleOnDragOver}
>
    {/* The div we are dropping the task in */}
</div>
```

And now the `handleOnDragOver` function

```tsx
function handleOnDragOver(e: React.DragEvent) {
    e.preventDefault();
}
```

This will stop a default behavior where the onDragOver keeps firing idefinately.

Now for the `onDrop`

```tsx
<div 
    className="..."
    onDragOver={handleOnDragOver}
    onDrop={handleOnDrop}
>
    {/* The div we are dropping the task in */}
</div>
```

Now lets write the `handleOnDrop` function

```tsx
function handleOnDrop(e: React.DragEvent) {
    if(tasks) {
        setTasks([...tasks,e.dataTransfer.getData("name")] )
    } else {
        setTasks([e.dataTransfer.getData("name")])
    }
}
```

Here we are creating a new array with the dropped task div's data and setting that array in the `tasks` state. The `e.dataTransfer.getData("name")` part will look familiar. Because we used `e.dataTransfer.setData("name", name)` in the `handleOnDrag` function to name the dragged task. And now we are getting that name data that we set previously when we started dragging it.

Now we have the data for the task that we are dropping. We now just have to show the task in the div using the data we got. We will use the same Task div we created earlier, inside the div we dropping it in. We will map through all the tasks in the `tasks` state and show them one by one.

```tsx
<div 
    className="..."
    onDragOver={handleOnDragOver}
    onDrop={handleOnDrop}
>
    {tasks &&
        tasks.map((taskName) => {
            return (
                <div
                    className="..."
                    draggable
                    onDragStart={(e) => {
                        handleOnDrag(e, taskName);
                    }}
                >
                    {taskName}
                </div>
            );
        })}
</div>
```

Finally we have the dragged task in the div we dropped it in. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7d72aum4ucdzzyyos6k.gif)

But there is a catch. The Task is getting duplicated every time we drag and drop it the task again in the div. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3lro79fr2bmw2x67lbc.gif)

To solve we have to delete the previous instance of the task in the state when we move it. Its pretty easy to do with `filter()`

```tsx
function handleOnDrop(e: React.DragEvent) {
    if(tasks) {
        setTasks([
            ...tasks.filter(
                (taskName) => taskName !== e.dataTransfer.getData("name")
            ),
            e.dataTransfer.getData("name"),
        ]);
    } else {
        setTasks([e.dataTransfer.getData("name")])
    }
}
```

Now everything is working. We can drag tasks from the list and drop them in the Todo div.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjiwh4yx8djfnbqd54hk.gif)

## How to extend the idea further ➡️

The basic stuff is set up. Now we can make it more user friendly by giving visual feedback when we start dragging the task and when we drop it. For that we can use `onDragStart`, `onDragEnd`, `onDragOver` etc. events to change the CSS styles of the elements. We can also add animations.

In part 2, we we will do all of these and also add two more columns for ongoing and completed tasks and make a fully functioning Kanban board-like todo board. We will need to manage three separate states and many more complexities. Part 2 coming soon ✅

Part 2 is released🔥

Check it out [here](https://dev.to/nasif2ahmed/drag-and-drop-kanban-board-from-scratch-with-react-part-2-f5e)

**Link to full code** : [Github Link](https://github.com/NasifAhmed/dnd-article)

I hope this was helpful to you 😊
Follow me on [Twitter](https://twitter.com/nasif2ahmed) and [LinkedIn](https://www.linkedin.com/in/nasif2ahmed/)
Thanks for reading 😇