Drag n Drop using react-beautiful-dnd

HemantHemant
12 min read

INTRODUCTION

Drag and drop is an essential user interface feature that allows users to intuitively interact with web applications. It enables users to move elements within the application or between different applications, making it easier to organize content, reorder items, and streamline workflows. As a result, drag and drop has become a fundamental feature of many web applications, ranging from file managers to task management tools.

One popular library that developers can use to implement drag-and-drop functionality in their React-based applications is react-beautiful-dnd. React-beautiful-dnd is a flexible and powerful library that provides a simple and intuitive API for developers to create drag-and-drop interfaces. It leverages modern web technologies such as HTML5 drag and drop and requestAnimationFrame to provide a smooth and responsive experience for users.

In this blog post, we will explore the benefits of using react-beautiful-dnd for implementing drag-and-drop functionality in React applications. We will examine some real-world examples of how drag and drop can improve the user experience in web applications, and how react-beautiful-dnd simplifies the development process. We will also provide step-by-step instructions on how to use the library, as well as tips and best practices for optimizing performance and usability. Whether you are new to React or an experienced developer, this blog post will provide valuable insights and practical guidance for implementing drag and drop in your web applications using react-beautiful-dnd.

Set up the environment

If you already have a React project set up, you can skip to the next section. Otherwise, here's a step-by-step guide to creating a new React project:

  1. Open your terminal and navigate to the directory where you want to create your project.

  2. Run the following command to create a new React project using Create React App:

npx create-react-app your-app-name

Installing react-beautiful-dnd

To install react-beautiful-dnd, you can use either npm or yarn. Here are the steps to install using both:

Using npm:

  1. Open your terminal and navigate to the root directory of your React project.

  2. Run the following command to install react-beautiful-dnd and its dependencies:

     npm install react-beautiful-dnd
    

    This will install react-beautiful-dnd along with its required dependencies, including react, react-dom, and prop-types.

Using yarn:

  1. Open your terminal and navigate to the root directory of your React project.

  2. Run the following command to install react-beautiful-dnd and its dependencies:

     yarn add react-beautiful-dnd
    

    This will install react-beautiful-dnd along with its required dependencies, including react, react-dom, and prop-types.

    react-beautiful-dnd is a library for creating beautiful, accessible drag-and-drop interfaces for your React applications. It provides a simple API for implementing drag-and-drop functionality with customizable behavior and styling. The library uses the HTML5 drag-and-drop API and supports touch devices as well.

    react-beautiful-dnd has two main components: DragDropContext and Droppable. DragDropContext is a higher-order component that wraps your entire app and provides the context for drag-and-drop interactions. Droppable is a component that defines a container that can receive draggable items.

    In addition to these components, react-beautiful-dnd also provides Draggable and Droppable components, which you can use to create drag-and-drop interfaces with ease.

Creating a draggable list

import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const initialItems = [
  { id: '1', content: 'Item 1' },
  { id: '2', content: 'Item 2' },
  { id: '3', content: 'Item 3' },
];

const App = () => {
  const [items, setItems] = useState(initialItems);

  const onDragEnd = (result) => {
    if (!result.destination) return;
    const { source, destination } = result;
    const newItems = [...items];
    const [removed] = newItems.splice(source.index, 1);
    newItems.splice(destination.index, 0, removed);
    setItems(newItems);
  };

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="items">
        {(provided) => (
          <ul {...provided.droppableProps} ref={provided.innerRef}>
            {items.map((item, index) => (
              <Draggable key={item.id} draggableId={item.id} index={index}>
                {(provided) => (
                  <li
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                  >
                    {item.content}
                  </li>
                )}
              </Draggable>
            ))}
            {provided.placeholder}
          </ul>
        )}
      </Droppable>
    </DragDropContext>
  );
};

export default App;

Let's break down each component and what it does:

  • DragDropContext: This is a higher-order component that wraps your entire app and provides the context for drag-and-drop interactions. It takes a onDragEnd prop, which is a callback function that is called when a drag-and-drop operation ends.

  • Droppable: This is a component that defines a container that can receive draggable items. It takes a droppableId prop, which is a unique identifier for the droppable area, and a children function that takes an object with properties provided and snapshot. provided contains props that need to be applied to the droppable element, and snapshot contains information about the current state of the droppable area.

  • Draggable: This is a component that defines a draggable item. It takes a draggableId prop, which is a unique identifier for the draggable item, and an index prop, which is the position of the item in the list. children the function takes an object with properties provided and snapshot. provided contains props that need to be applied to the draggable element, and snapshot contains information about the current state of the draggable item.

In this example, we're using useState to manage the state of the list items. We're also defining a onDragEnd callback function that updates the state of the items when a drag-and-drop operation ends.

Inside the Droppable component, we're mapping over the items state and rendering a Draggable component for each item. The Draggable component is responsible for rendering the item and providing the necessary props for dragging and dropping.

Finally, we're using provided.placeholder to render a placeholder element while the item is being dragged. This is important for maintaining the layout of the list

Add Styling

One way is to add styles directly to the components using inline styles or className props. For example, you could add a className to the li element inside the Draggable component and then add styles for that class in your CSS:

First of all import App.css from the root directory in App.js
After that imports should be like this:

import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import './App.css';

Now go to App.css and add some styles to it, like this:

.item {
  background-color: rgb(192, 122, 122);
  border: 1px solid #000000;
  border-radius: 4px;
  margin-bottom: 8px;
  padding: 8px;
  list-style: none;
  color: white;
  width: 30rem;
}

After adding styles in your App.css, Add the item class to the list of item(<li>) which is displayed after mapping the items.

<Draggable key={item.id} draggableId={item.id} index={index}>
  {(provided) => (
  <li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} className='item'>
    {item.content}
  </li>
  )}
</Draggable>

Then the output list should be looking like this:

Now if you are wondering if drag-n-drop is not working then go to the index.js in the root directory and disable the React.StrictMode after that, it will start working :)

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

After removing React.StrictMode

root.render(
    <App />
);

Another way is to use the provided object that's passed to the Droppable and Draggable children functions. This object contains properties like innerRef, draggableProps, and dragHandleProps that you can use to style the components. For example:

<Draggable key={item.id} draggableId={item.id} index={index}>
  {(provided, snapshot) => (
    // Inside the Draggable component:
    <li
      ref={provided.innerRef}
      {...provided.draggableProps}
      {...provided.dragHandleProps}
      style={{
        backgroundColor: snapshot.isDragging ? 'rgb(231, 179, 179)' : '',
        ...provided.draggableProps.style
      }}
      className='item'
    >
      {item.content}
    </li>
  )}
</Draggable>

In this example, we're using the snapshot object to check if the item is being dragged, and if so, we're changing the background color to a light gray. We're also using the provided.draggableProps.style object to apply any additional styles that are required for the draggable item.

To make the list look visually appealing, you can add additional styles such as:

  • A background color or gradient for the entire list.

  • A hover effect on the draggable items.

  • An animation or transition when the items are being dragged.

  • A border or shadow around the draggable items to give them a sense of depth.

Overall, the styling you add will depend on the look and feel you want to achieve for your draggable list. Remember to keep it simple and clean, so that the drag-and-drop interactions remain clear and easy to use.

Customize the behavior

  1. Disable certain items from being dragged: To disable certain items from being dragged, you can add a isDragDisabled prop to the Draggable component. For example, if you want to disable the first item in the list from being dragged, you can do the following:

     {items.map((item, index) => (
       <Draggable key={item.id} draggableId={item.id} index={index} isDragDisabled={index === 0}>
         {(provided, snapshot) => (
           // ...
         )}
       </Draggable>
     ))}
    

    In this example, the isDragDisabled the prop is set to true for the first item in the list, so it cannot be dragged.

  2. Disable dropping in certain places: To disable dropping in certain places, you can add a isDropDisabled prop to the Droppable component. For example, if you want to disable dropping items in the second list in a multi-column layout, you can do the following:

     <DragDropContext onDragEnd={onDragEnd}>
       <div className="columns">
         <Droppable droppableId="column-1">
           {(provided, snapshot) => (
             <div
               ref={provided.innerRef}
               style={getListStyle(snapshot.isDraggingOver)}
               {...provided.droppableProps}
               isDropDisabled={true} // Add this prop to disable dropping in this list
             >
               {items1.map((item, index) => (
                 <Draggable key={item.id} draggableId={item.id} index={index}>
                   {(provided, snapshot) => (
                     // ...
                   )}
                 </Draggable>
               ))}
               {provided.placeholder}
             </div>
           )}
         </Droppable>
         <Droppable droppableId="column-2">
           {(provided, snapshot) => (
             <div
               ref={provided.innerRef}
               style={getListStyle(snapshot.isDraggingOver)}
               {...provided.droppableProps}
             >
               {items2.map((item, index) => (
                 <Draggable key={item.id} draggableId={item.id} index={index}>
                   {(provided, snapshot) => (
                     // ...
                   )}
                 </Draggable>
               ))}
               {provided.placeholder}
             </div>
           )}
         </Droppable>
       </div>
     </DragDropContext>
    

    In this example, the isDropDisabled the prop is set to true for the second list, so items cannot be dropped into it.

  3. Change the behavior on drag or drop: To change the behavior on drag or drop, you can use the onDragStart, onDragUpdate, onDragEnd, onBeforeCapture, onBeforeDragStart, and onDragCancel callbacks provided by the DragDropContext component. These callbacks receive an object containing information about the drag-and-drop operation, which you can use to modify the behavior as needed. For example, you could change the cursor or add a placeholder when an item is being dragged:

<DragDropContext
  onDragStart={() => {
    document.body.style.cursor = 'grabbing';
  }}
  onDragEnd={(result) => {
    document.body.style.cursor = 'auto';
    if (result.destination) {
      // Handle the drop
    }
  }}
>
  // ...
</DragDropContext>

In this example, the onDragStart callback changes the cursor to a grabbing hand when an item is being dragged, and the

Some examples of how the drag-n-drop can be used in real-world scenarios

Here are a few examples of how the drag-n-drop feature can be used in real-world scenarios:

  1. To-do list: A to-do list app can allow users to reorder their tasks by dragging and dropping them into a different order. For example, a user can prioritize their most important tasks by dragging them to the top of the list, or group similar tasks together by dragging them next to each other. The drag-and-drop feature can also be used to move tasks between different lists, such as a "To Do" list and a "Done" list.

  2. File manager: A file manager app can allow users to organize their files by dragging and dropping them into different folders or rearranging them within a folder. For example, a user can group related files together by dragging them next to each other, or move a file to a different folder by dragging it to the appropriate folder icon. The drag-and-drop feature can also be used to copy or move files between different windows or applications.

  3. E-commerce website: An e-commerce website can allow users to add items to their shopping cart by dragging and dropping them from a product grid or list. For example, a user can drag a product image to their shopping cart icon to add the item to their cart or drag the item to a specific area on the page to view more information or options for the product. The drag-and-drop feature can also be used to move items between the shopping cart and a wishlist or favorites list.

  4. Kanban board: A Kanban board app can allow users to move tasks between different stages of a project by dragging and dropping them into the appropriate column. For example, a user can move a task from the "To Do" column to the "In Progress" column by dragging it to the appropriate column header or move a task to the "Done" column by dragging it to the bottom of the column. The drag-and-drop feature can also be used to reorder tasks within a column or move tasks to a different board or project.

Troubleshooting

Here are some common issues that might arise when using react-beautiful-dnd, along with some troubleshooting tips:

  1. Droppable area not working: If the droppable area is not working, check that the droppable component is properly wrapped around the draggable components. Also, make sure that the droppableId is properly assigned to the droppable component and matches the draggableId assigned to the draggable components.

  2. Dragged item not displaying properly: If the dragged item is not displaying properly, check that the draggable component has the proper styles assigned to it, such as position: absolute and z-index. Also, make sure that the onDragStart and onDragEnd callbacks are properly implemented.

  3. Disabled draggable item is still draggable: If a disabled draggable item is still draggable, make sure that the isDragDisabled prop is properly assigned to the draggable component.

  4. Scroll position jumping during drag: If the scroll position jumps during the drag, it might be because the scroll container is not properly sized. Make sure that the scroll container has a fixed height or is properly sized using CSS.

  5. Incorrect index after reordering: If the index of an item is incorrect after reordering, it might be because the index is based on the order of the items in the original list. Make sure that the index is properly updated based on the new order of the items.

For further learning, the react-beautiful-dnd documentation provides detailed troubleshooting guides and examples, along with an active community forum where users can ask questions and receive help from other users. Additionally, there are many online resources such as tutorials, videos, and blog posts that provide tips and best practices for using react-beautiful-dnd.

Conclusion

In this blog, we've covered how to implement a drag-and-drop feature using the react-beautiful-dnd library. We've walked through the process of setting up a React project, installing react-beautiful-dnd, creating a draggable list using the <DragDropContext>, <Droppable>, and <Draggable> components, adding styling using CSS or a CSS preprocessor, and customizing the behavior of the drag-and-drop feature.

We've also provided examples of how the drag-and-drop feature can be used in real-world scenarios, such as reordering items in a to-do list or organizing files in a file manager. Additionally, we've provided some troubleshooting tips for common issues that might arise when using react-beautiful-dnd, along with resources for further learning.

Implementing a drag-and-drop feature can greatly enhance the user experience of your application, and react-beautiful-dnd provides a simple and flexible way to do so. We encourage readers to try implementing this feature using react-beautiful-dnd in their own projects and explore the many possibilities it offers for improving the usability and interactivity of their applications.

Code of this example

  1. GitHub Repository

  2. Follow me on Twitter

22
Subscribe to my newsletter

Read articles from Hemant directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hemant
Hemant

My passion for creating beautiful websites led me to become a fellow frontend developer