How to implement drag-and-drop functionality in React application

Drag-and-drop is a user interaction technique that allows users to select and manipulate objects on a computer screen by dragging them from one location and dropping them onto another. Drag-and-drop functionality can be useful for tasks such as reordering items, file uploads, creating custom layouts, and more.

Implementation

We will build a simple project in ReactJS where users can drag items between two lists using the following steps.

  1. Creating the DraggableItem component.

  2. Creating the Droppable Area Component.

  3. Building the App Component.

Creating the DraggableItem component

We will create the DraggableItem component, which represents the items that can be dragged and dropped.

import React from 'react';
const DraggableItem = ({ item, onDragStart }) => {
const handleDragStart = (e) => {
e.dataTransfer.setData('text/plain', item); // Set the data being dragged
onDragStart();
};
return (
<div
draggable
onDragStart={handleDragStart}
>
{item}
</div>
);
};
export default DraggableItem;

Explanation

  • Line 3: Declaring the DraggableItem component as a functional component, which accepts two props: item and onDragStart.

  • Line 4: Defining the handleDragStart function, which is triggered when the drag operation starts.

  • Line 5: The setData method is used to set the data being dragged as plain text, with the value of item. This data can be accessed by other components during the drag and drop operation.

  • Line 10: Setting the draggable attribute to true to make the element draggable.

Creating the Droppable Area Component

Now, we will create the DroppableArea component, which represents the area where items can be dropped.

import React from 'react';
const DroppableArea = ({ onDrop, onDragOver }) => {
const handleDrop = (e) => {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain'); // Get the data being dropped
onDrop(data);
};
return (
<div
onDrop={handleDrop}
onDragOver={onDragOver}
>
Droppable Area
</div>
);
};
export default DroppableArea;

Explanation

  • Line 3: This line declares a functional component called DroppableArea. It accepts two props, onDrop and onDragOver, which are passed from the parent component. The props are accessed using object destructuring in the function's parameter list. This syntax allows us to directly access the onDrop and onDragOver props instead of accessing them through props.onDrop and props.onDragOver.

  • Line 4–5: In this section, we define the handleDrop function. This function is triggered when an item is dropped onto the droppable area. It takes an event object (e) as a parameter. Within handleDrop, we call e.preventDefault() to prevent the default browser behavior for the drop event.After retrieving the data, we call the onDrop function provided as a prop and pass the data as an argument. This function is responsible for handling the dropped data, performing any necessary operations or updates.

  • Line 6–8: Next, we use the getData method of the dataTransfer object attached to the drop event (e.dataTransfer) to retrieve the data that was set when the item was dragged. In this case, we assume the data is in plain text format and assign it to the data variable.

  • Line 12: We set the onDrop event handler to the handleDrop function we defined earlier. This means that when an item is dropped onto the <div>, the handleDrop function will be called to handle the drop event.

  • Line 13: We set the onDragOver event handler to the onDragOver prop provided to the component. This prop allows for additional logic or actions to be performed when an item is dragged over the droppable area.

Building the App component

Now, let's build the main App component that will render the draggable items and the droppable area. We will manage the state of the dragged and dropped items using the React useState hook.

import React, { useState } from 'react';
import DraggableItem from './components/DraggableItem';
import DroppableArea from './components/DroppableArea';
const App = () => {
const [draggedItem, setDraggedItem] = useState(null);
const [droppedItems, setDroppedItems] = useState([]);
const handleDragStart = () => {
setDraggedItem(null); // Reset the dragged item
};
const handleDrop = (item) => {
setDroppedItems([...droppedItems, item]);
};
const handleDragOver = (e) => {
e.preventDefault();
};
return (
<div>
<h1>Drag and Drop Demo</h1>
<div style={{ display: 'flex' }}>
<div style={{ flex: 1 }}>
<h2>Draggable Items</h2>
<DraggableItem item="Item 1" onDragStart={handleDragStart} />
<DraggableItem item="Item 2" onDragStart={handleDragStart} />
<DraggableItem item="Item 3" onDragStart={handleDragStart} />
</div>
<div style={{ flex: 1 }}>
<h2>Droppable Area</h2>
<DroppableArea onDrop={handleDrop} onDragOver={handleDragOver} />
<ul>
{droppedItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
  • Line 5–7: In this section, we define the App functional component. It initializes two state variables using the useState hook.

  • Line 6: The draggedItem state variable is used to track the currently dragged item, and it is initially set to null. The setDraggedItem function is used to update the value of the draggedItem state.

  • Line 7: The droppedItems state variable is used to store an array of dropped items, and it is initially set to an empty array. The setDroppedItems function is used to update the value of the droppedItems state.

Output

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

Edge cases

Here are a few additional edge cases to consider.

  • Handling invalid data types: The implementation assumes that the dragged data is of type 'text/plain'. If you expect to handle different data types or want to enforce specific data type requirements, you can add additional validation and error handling. For example, you can check the data type during the drop event and display an error message if an unsupported data type is dropped.

  • Dragging non-draggable elements: In the current implementation, all items within the draggable area are draggable by default. It's important to ensure that only the intended items are draggable. You can handle this by selectively applying the draggable attribute only to the elements that should be draggable and disable dragging for non-draggable elements.

  • Dragging the same item multiple times: Currently, the implementation allows dragging the same item multiple times and dropping it into the droppable area. Depending on your use case, you may want to handle this edge case differently. For example, you could prevent dragging the same item multiple times by disabling the draggable state of an item once it has been dragged.

  • Concurrent drag-and-drop operations: If you expect users to perform simultaneous drag-and-drop operations, you may need to implement additional logic to track and handle these concurrent operations. For instance, you could manage a drag state for each draggable item to keep track of the currently active dragging item and ensure that the dropped item corresponds to the correct dragged item.

Conclusion

We have explored how to implement drag-and-drop functionality in React applications which provides a seamless and intuitive user experience for handling elements or components. We can add additional features such as drag handles, custom drag images, or implement drag-and-drop between multiple containers.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved