How to implement drag and drop in React with React DnD - LogRocket Blog (2024)

Editor’s note: This tutorial was last updated on 3 November 2023 to include advanced drag-and-drop features, as well as alternative tools for implementing drag-and-drop in React, such as React Smooth DnD.

How to implement drag and drop in React with React DnD - LogRocket Blog (1)

React DnD brings draggable elements to HTML, enabling developers to build applications that contain rich UI elements that can be dragged from one place to another. React DnD is an integral part of most modern applications, providing richness in UI without comprising the UX.

The most common use cases for drag and drop in React include uploading files, moving items between multiple lists, and rearranging images and assets. In this tutorial, we’ll focus on several different tools and use cases for implementing drag and drop in React, with a focus on the React DnD library.

An introduction to React DnD

React DnD simplifies the implementation of drag-and-drop interactions by providing a set of abstractions and components to handle complex drag-and-drop behavior, including the following features:

  • Drag sources and drop targets: React DnD allows you to define components as drag sources, elements that can be dragged, and drop targets, regions or elements where draggable items can be dropped. React DnD is also composable, allowing you to create custom drag sources and drop targets
  • Customizable drag previews: With React DnD, you can customize the appearance of the drag preview by providing your rendering function, allowing you to create a visual representation of the dragging item
  • Drag and drop effects: React DnD also supports different types of drag-and-drop effects, such as “copy,” “move,” or “link”, allowing you to specify what happens when an item is dropped on a target, and customize the feedback provided to the user during the drag operation
  • Touch device support: React DnD is designed to work well on both desktop and touch-based devices, providing a consistent user experience
  • Multi-type dragging: You can specify multiple types for a draggable item, allowing different drop targets to handle the drag operation differently based on the type of item being dragged

React DnD also meets WCAG (Web Content Accessibility Guidelines), so it can be made accessible to users with disabilities, ensuring an inclusive user experience. With React DnD, we can create beautiful apps like the chess app below:

How to implement drag and drop in React with React DnD - LogRocket Blog (2)

React DnD pros

React DnD works for almost all use cases, like grids, one-dimensional lists, and more. Additionally, it has a very powerful API to add any customization to drag-and-drop in React.

React DnD cons

For small examples, the React DnD API is very easy to get started with. However, it can be tricky to add complex customizations. The learning curve is also higher and more complex than other libraries, such as react-beautiful-dnd. Additionally, some hacks are required to support both web and touch devices.

Now let’s build a simple drag-and-drop application with React DnD.

Building a drag-and-drop app using React DnD

Before we dive into the drag-and-drop code, we need to first understand how React DnD works.

React DnD can make any element draggable and droppable. To achieve this, React DnD needs to have the references of all droppable items. All elements that are draggable and droppable need to be enclosed inside React DnD’s context provider, which is used for initializing and managing the internal state.

We don’t need to worry too much about how it manages state because React DnD includes easy APIs to expose these states, enabling us to compute and update our local states.

To install React DnD into your React app, run either of the commands below:

yarn add react-dnd react-dnd-html5-backend immutability-helper//ornpm install react-dnd react-dnd-html5-backend immutability-helper

Keep in mind that this example won’t work until you complete all the steps. Check out the repo for React DnD v16 here.

First, we’ll enclose our ImageList component inside a DnD context provider:

/* filename: App.js */import { DndProvider } from "react-dnd";import {HTML5Backend} from "react-dnd-html5-backend";function App() { ... return ( <main className="App"> ... <DndProvider backend={HTML5Backend}> <ImageList images={images} moveImage={moveImage}/> <DndProvider> </main> );}

Next, import the DNDProvider and initialize it with the backend props. This is the variable that helps us choose which API to use for drag and drop.

Now, we need to add the items as draggable and droppable. In our application, both draggable and droppable items are the same. We’ll drag the Image component and drop it onto another Image component, making our job a little easier. To implement this, use the code below:

import React, { useRef } from "react";// import useDrag and useDrop hooks from react-dndimport { useDrag, useDrop } from "react-dnd";const type = "Image"; // Need to pass which type element can be draggable, its a simple string or Symbol. This is like an Unique ID so that the library know what type of element is dragged or dropped on.const Image = ({ image, index }) => { const ref = useRef(null); // Initialize the reference // useDrop hook is responsible for handling whether any item gets hovered or dropped on the element const [, drop] = useDrop({ // Accept will make sure only these element type can be droppable on this element accept: type, hover(item) { ... } }); // useDrag will be responsible for making an element draggable. It also expose, isDragging method to add any styles while dragging const [{ isDragging }, drag] = useDrag(() => ({ // what type of item this to determine if a drop target accepts it type: type, // data of the item to be available to the drop methods item: { id: image.id, index }, // method to collect additional data for drop handling like whether is currently being dragged collect: (monitor) => { return { isDragging: monitor.isDragging(), }; }, })); /* Initialize drag and drop into the element using its reference. Here we initialize both drag and drop on the same element (i.e., Image component) */ drag(drop(ref)); // Add the reference to the element return ( <div ref={ref} style={{ opacity: isDragging ? 0 : 1 }} className="file-item" > <img alt={`img - ${image.id}`} src={image.src} className="file-img" /> </div> );};const ImageList = ({ images, moveImage }) => { const renderImage = (image, index) => { return image ? ( <Image image={image} index={index} key={`${image.id}-image`} moveImage={moveImage} /> ): null; }; return <section className="file-list">{images.map(renderImage)}</section>;export default ImageList;

Now, our images are draggable. But, if we drop an image, it will return to its original position because useDrag and useDrop will handle it until it is dropped. Unless we change our local state, the image will return to its original position.

To update the local state, we need to know the dragged element and the hovered element, which is the element on which the dragged element is hovered. useDrag exposes this information through the hover method:

const [, drop] = useDrop({ // accept receives a definition of what must be the type of the dragged item to be droppable accept: type, // This method is called when we hover over an element while dragging hover(item) { // item is the dragged element if (!ref.current) { return; } const dragIndex = item.index; // current element where the dragged element is hovered on const hoverIndex = index; // If the dragged element is hovered in the same place, then do nothing if (dragIndex === hoverIndex) { return; } // If it is dragged around other elements, then move the image and set the state with position changes moveImage(dragIndex, hoverIndex); /* Update the index for dragged item directly to avoid flickering when the image was half dragged into the next */ item.index = hoverIndex; }});

The hover method is triggered whenever an element is dragged and hovers over this element. In this way, when we start dragging an element, we get the index of that element and the element we are hovering on. We’ll pass this dragIndex and hoverIndex to update our image’s state.

At this point, you might be wondering why we need to update the state while hovering. Why not update it while dropping? It’s possible to just update the state while dropping. drag-and-drop will work and rearrange the positions, but the UX won’t be good.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

For example, if you drag one image over another image and we immediately change the position, then that will give good feedback to the users who are dragging it. Otherwise, they might not know whether the drag functionality is working until they drop the image in some position. Here’s an example:

How to implement drag and drop in React with React DnD - LogRocket Blog (5)

Therefore, we should update the state on every hover. While hovering over another image, we set the state and change the positions, and the user will see a nice animation.

So far, we’ve just shown the code for updating the state as moveImage. Now, let’s see the implementation:

/* filename: App.js*/import update from "immutability-helper";...const moveImage = (dragIndex, hoverIndex) => { // Get the dragged element const draggedImage = images[dragIndex]; /* - copy the dragged image before hovered element (i.e., [hoverIndex, 0, draggedImage]) - remove the previous reference of dragged element (i.e., [dragIndex, 1]) - here we are using this update helper method from immutability-helper package */ setImages( update(images, { $splice: [[dragIndex, 1], [hoverIndex, 0, draggedImage]] }) );};// We will pass this function to ImageList and then to Image -> Quite a bit of props drilling, the code can be refactored and place all the state management in ImageList itself to avoid props drilling. It's an exercise for you :)

Now, our app is fully functional on HTML5 onDrag event-supported devices. Unfortunately, it won’t work on touch devices.

As I said before, we can support touch devices using a utility function. It’s not the best solution, but it works. The experience of dragging won’t be great on touch devices — it simply updates, but you won’t feel like you’re dragging. You can also make it clean:

import HTML5Backend from "react-dnd-html5-backend";import TouchBackend from "react-dnd-touch-backend";// simple way to check whether the device support touch (it doesn't check all fallback, it supports only modern browsers)const isTouchDevice = () => { if ("ontouchstart" in window) { return true; } return false;};// Assigning backend based on touch support on the deviceconst backendForDND = isTouchDevice() ? TouchBackend : HTML5Backend;...return ( ... <DndProvider backend={backendForDND}> <ImageList images={images} moveImage={moveImage} /> </DndProvider>)...

Alternative React drag-and-drop libraries

react-beautiful-dnd

react-beautiful-dnd is another popular third-party library for implementing drag-and-drop interactions in React applications, especially when dealing with ordered lists, grids, and other collections of items.

react-beautiful-dnd offers a high-level, declarative API and contains functionalities such as:

  • List reordering: react-beautiful-dnd allows users to rearrange items within a list easily. This is particularly useful for apps like to-do lists, task management, and content management systems
  • Custom drag handles: Developers can specify which parts of a component should serve as drag handles. This allows users to initiate the drag operation by interacting with specific elements within an item
  • Nested lists and complex structures: react-beautiful-dnd allows you to create nested lists and more complex structures, so users can organize items hierarchically
  • Snapshot testing: The library includes tools for snapshot testing, which helps ensure that drag-and-drop components are rendering correctly during development and updates
  • Server-side rendering (SSR) support: react-beautiful-dnd is designed to work well with server-side rendering, ensuring compatibility with various React application architectures

If you’d like to read about how to create a drag-and-drop example using react-beautiful-dnd, check out this video tutorial.

React-Grid-Layout

React-Grid-Layout is a grid layout system built exclusively for React. Unlike similar systems like Packery and Gridster, React-Grid-Layout is responsive and supports breakpoint layouts, which can be provided by the user or autogenerated.

React-Grid-Layout does not require jQuery. It allows you to create draggable grid items that can be moved within the grid by dragging and dropping into different positions.

React-Draggable

React-Draggable is a popular and lightweight React library that allows you to make DOM elements draggable within a specified container.

With this library, you can customize the appearance and behavior of the draggable element, such as setting initial position, containment, and enabling or disabling dragging as needed. It also supports grid-based snapping, allowing you to align the draggable element to a grid or predefined snap points, while also allowing you to specify the boundaries within which the element can be dragged, ensuring it stays within a defined area.

react-smooth-dnd

react-smooth-dnd is a popular third-party library for adding smooth and visually pleasing drag-and-drop interactions to your React applications. It builds upon the core features of React DnD and provides a higher-level API that simplifies the implementation of drag-and-drop behavior with smooth animations and transitions.

react-smooth-dnd is particularly useful when you want to create lists with smooth reordering animations or when you need to implement drag-and-drop interactions in a visually appealing manner.

Advanced drag-and-drop features

The examples above cover the basic implementation of drag-and-drop functionalities, but there are more advanced drag-and-drop features in React. Here are a few:

Drag and clone: This feature creates a duplicate of an item in place after it is dragged. This leaves the original item in place, enabling actions like creating duplicates or maintaining a reference to the original.

There’s no built-in support for this feature out of the box with React DnD, so if you want to implement it, it’s best you use react-beautiful-dnd, which has a built-in drag and clone feature that can be easily implemented with the code below:

import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';// ...<DragDropContext onDragEnd={handleDragEnd}> <Droppable droppableId="list"> {(provided) => ( <ul {...provided.droppableProps} ref={provided.innerRef}> {items.map((item, index) => ( <Draggable key={item.id} draggableId={item.id} index={index}> {(provided, snapshot) => ( <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} style={{ background: snapshot.isDragging ? 'lightblue' : 'white', ...provided.draggableProps.style, }} > {item.content} </div> )} </Draggable> ))} {provided.placeholder} </ul> )} </Droppable></DragDropContext>

Custom animations: You can apply custom animations during drag-and-drop actions, including smooth transitions, visual effects, or transformations to enhance the user experience.

react-beautiful-dnd, in particular, provides flexibility to incorporate animations and transitions directly into your drag-and-drop components, making it a powerful tool for creating visually appealing drag-and-drop interactions.

Complex hierarchies: This is the ability to manage and manipulate structured data in a hierarchical or tree-like manner through drag-and-drop interactions. In drag-and-drop operations, complex hierarchies intuitively convey the intended changes, such as promoting or demoting items, changing parent-child relationships, and altering the order of items.

Conclusion

Drag-and-drop operations may seem a bit complicated from afar, but with the help of third-party libraries, we’ve successfully shown a simple yet powerful demo for dragging and dropping files and reordering those files.

More great articles from LogRocket:

  • Don't miss a moment with The Replay, a curated newsletter from LogRocket
  • Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
  • Use React's useEffect to optimize your application's performance
  • Switch between multiple versions of Node
  • Discover how to use the React children prop with TypeScript
  • Explore creating a custom mouse cursor with CSS
  • Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

We just scratched the surface of what React is capable of in terms of drag-and-drop functionality, as there are more exhaustive features you can implement using drag-and-drop libraries. Your true limit is your creativity.

I hope this helps you to build your next drag-and-drop functionality faster and with confidence. Feel free to share with us what you’ve built with it in the comments.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to getan app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, notserver-side

    • npm
    • Script tag
    $ npm i --save logrocket // Code:import LogRocket from 'logrocket'; LogRocket.init('app/id'); 
    // Add to your HTML:<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script><script>window.LogRocket && window.LogRocket.init('app/id');</script> 
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin

Get started now

How to implement drag and drop in React with React DnD - LogRocket Blog (2024)
Top Articles
Access API money transfers, send money via API in Genome
The 10 Best Trader Joe’s Frozen Foods to Air Fry, According to a Food Editor
30 Insanely Useful Websites You Probably Don't Know About
PontiacMadeDDG family: mother, father and siblings
Robinhood Turbotax Discount 2023
Top Financial Advisors in the U.S.
Emmalangevin Fanhouse Leak
Tanger Outlets Sevierville Directory Map
Rubfinder
New Day Usa Blonde Spokeswoman 2022
83600 Block Of 11Th Street East Palmdale Ca
Culver's Flavor Of The Day Monroe
4Chan Louisville
Winterset Rants And Raves
UEQ - User Experience Questionnaire: UX Testing schnell und einfach
Jesus Calling Oct 27
Mile Split Fl
Houses and Apartments For Rent in Maastricht
Puretalkusa.com/Amac
How pharmacies can help
50 Shades Of Grey Movie 123Movies
97226 Zip Code
Unforeseen Drama: The Tower of Terror’s Mysterious Closure at Walt Disney World
Morristown Daily Record Obituary
Play Tetris Mind Bender
Kentuky Fried Chicken Near Me
Regina Perrow
Victory for Belron® company Carglass® Germany and ATU as European Court of Justice defends a fair and level playing field in the automotive aftermarket
Kitchen Exhaust Cleaning Companies Clearwater
Smartfind Express Login Broward
3 Ways to Format a Computer - wikiHow
Why comparing against exchange rates from Google is wrong
Red Sox Starting Pitcher Tonight
Storelink Afs
RFK Jr., in Glendale, says he's under investigation for 'collecting a whale specimen'
Gyeon Jahee
Joe's Truck Accessories Summerville South Carolina
Vip Lounge Odu
Myql Loan Login
The Banshees Of Inisherin Showtimes Near Reading Cinemas Town Square
2020 Can-Am DS 90 X Vs 2020 Honda TRX90X: By the Numbers
Doordash Promo Code Generator
Directions To The Closest Auto Parts Store
Below Five Store Near Me
Advance Auto.parts Near Me
Goosetown Communications Guilford Ct
View From My Seat Madison Square Garden
Jigidi Jigsaw Puzzles Free
David Turner Evangelist Net Worth
Parks And Rec Fantasy Football Names
Ssss Steakhouse Menu
Cognitive Function Test Potomac Falls
Latest Posts
Article information

Author: Clemencia Bogisich Ret

Last Updated:

Views: 6449

Rating: 5 / 5 (80 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Clemencia Bogisich Ret

Birthday: 2001-07-17

Address: Suite 794 53887 Geri Spring, West Cristentown, KY 54855

Phone: +5934435460663

Job: Central Hospitality Director

Hobby: Yoga, Electronics, Rafting, Lockpicking, Inline skating, Puzzles, scrapbook

Introduction: My name is Clemencia Bogisich Ret, I am a super, outstanding, graceful, friendly, vast, comfortable, agreeable person who loves writing and wants to share my knowledge and understanding with you.