Software Engineers always work with Project management software like JIRA or Trello. In that software, developers create to-do lists, prioritize tasks, and create various sections to streamline the development process.
If you analyze carefully, you will notice one common functionality: “Drag-and-Drop.” This is where developers can prioritize a to-do list by changing the order of the tasks however they want. That is why drag-and-drop functionality is helpful when users have to manage the list of things they want to do in a day or week sprint.
If you want to create a great UI/UX experience, you must create an intuitive user experience that mimics real-world interactions. Let’s explore drag-and-drop.
What is drag-and-drop?
The drag-and-drop is a UI element that allows users to quickly rearrange items like products, tasks, or other items, such as reordering a list or organizing elements, without multiple clicks. This functionality will help us create more engaging content, leading to a more tailored and satisfying experience.
Whether you are working with File or Task Management, drag-and-drop will make your UI more interactive. Users can immediately get feedback on what is happening in front of their eyes, leaving more transparency about your application.
Let’s create a simple project and implement this type of functionality in React:
Project Overview
We will display items in a list format with drag-and-drop functionality. A user can update the order of items dynamically, and the changes will persist to the backend JSON server.
Here is the final project output GIF:
You can see from the above final project output GIF that even after the page is refreshed, it retains the changed position by dragging and dropping. This is what we are going to build in this post.
You can find the complete code on Github. Here is the URL: https://github.com/KrunalLathiya/ReactDragDrop
Here is the step-by-step guide for implementing drag-and-drop functionality with MUI in React:
Step 1: Initialize a React Project
Let’s use the Vite development template to create a new React project by the below command:
npm create vite@latest drag-drop-mui-project -- --template react
Go inside the project folder and install the dependencies:
cd drag-drop-mui-project npm install
Step 2: Install dndkit, Axios, MUI, and json-server
To implement drag-and-drop functionality in React, one of the most popular libraries in a community is DnDKit.
The DndKit is a lightweight, performant, accessible, and extensible drag-and-drop toolkit for React.
Axios will be used to send GET and PATCH requests to the JSON server.
MUI will be used to style our web application
The JSON-server package is used to create an API server that will serve the db.json file, which contains the data for our application. It also provides REST API endpoints.
Type the command below to install all the above-mentioned packages:
npm install @mui/material @emotion/react @emotion/styled @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
Step 3: Set up the json-server
Create a db.json file in your root of the project and add the below code in it:
{ "items": [ { "id": "1", "content": "Item 1", "order": 1 }, { "id": "2", "content": "Item 2", "order": 2 }, { "id": "3", "content": "Item 3", "order": 3 }, { "id": "4", "content": "Item 4", "order": 4 }, { "id": "5", "content": "Item 5", "order": 5 } ] }
Now, add a script to your package.json
file:
"scripts": { "dev": "vite", "build": "vite build", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", "server": "json-server db.json --port 3001" },
Go to your terminal and start the json-server using the below command:
npm run server
And it will create an API endpoint like this: http://localhost:3001/items
Step 4: Set up API service
We have already created a web server(json-server) that returns the data in JSON format. This server hosts REST API endpoints, which we can consume using our application.
Let’s create two functions for the two REST API endpoints:
- fetchItems: This function fetches all the items from the API server.
- updateItemOrder: This function updates the order of items in the backend “db.json” file to reflect their new positions after they have been reordered.
Under the “src” folder, create a new folder called “services”.
Inside the “services” folder, create a new file called “api.js” and add the code below:
// src/services/api.js import axios from 'axios'; const API_URL = 'http://localhost:3001/items'; export const fetchItems = async () => { try { const response = await axios.get(API_URL); return response.data.sort((a, b) => a.order - b.order); } catch (error) { console.error('Error fetching items:', error); return []; } }; export const updateItemOrder = async (items) => { try { const promises = items.map((item, index) => axios.patch(`${API_URL}/${item.id}`, { order: index }) ); await Promise.all(promises); } catch (error) { console.error('Error updating item order:', error); } };
The fetchItems() function returns a response sorted by each item’s “order” property in ascending order. If there is an error, the function will return an empty array ([ ]).
The updateItemOrder() function returns the order of items in the backend to match their new positions after being reordered in the front end.
Step 5: Create a SortableItem component
We need to create a SortableItem component representing a list of sortable items. This is where the @dnd-kit library helps enable drag-and-drop functionality and Material-UI for styling.
The @dnd-kit/sortable library provides a useSortable hook that provides the important properties and methods to make the item sortable. It dynamically applies transition and transformation to the item to handle movement during drag-and-drop interactions.
Under the “src” folder, create the “components” folder.
Inside the “components” folder, create a new file called “SortableItem.jsx”.
// src/components/SortableItem.jsx import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { ListItem, ListItemText, Paper } from "@mui/material"; // eslint-disable-next-line react/prop-types const SortableItem = ({ id, content }) => { const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); const style = { transform: CSS.Transform.toString(transform), transition, }; return ( <ListItem ref={setNodeRef} style={style} {...attributes} {...listeners}> <Paper style={{ width: "100%" }}> <ListItemText primary={content} /> </Paper> </ListItem> ); }; export default SortableItem;
Step 6: Create a DragandDrop component
Under the “components” folder, create a new file called DragDropList.jsx and add the code below:
// src/components/DragDropList.jsx import { useEffect, useState } from "react"; import { DndContext, closestCenter } from "@dnd-kit/core"; import { SortableContext, verticalListSortingStrategy, arrayMove, } from "@dnd-kit/sortable"; import { List } from "@mui/material"; import SortableItem from "./SortableItem"; import { fetchItems, updateItemOrder } from "../services/api"; const DragDropList = () => { const [items, setItems] = useState([]); useEffect(() => { const loadItems = async () => { const initialItems = await fetchItems(); setItems(initialItems); }; loadItems(); }, []); const handleDragEnd = (event) => { const { active, over } = event; if (active.id !== over.id) { setItems((items) => { const oldIndex = items.findIndex((item) => item.id === active.id); const newIndex = items.findIndex((item) => item.id === over.id); const newItems = arrayMove(items, oldIndex, newIndex); updateItemOrder(newItems); return newItems; }); } }; return ( <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}> <SortableContext items={items} strategy={verticalListSortingStrategy}> <List> {items.map((item) => ( <SortableItem key={item.id} id={item.id} content={item.content} /> ))} </List> </SortableContext> </DndContext> ); }; export default DragDropList;
Let me explain what is happening in this code. We created a DragDropList component that fetches the initial list of items from the backend when it mounts using the useEffect hook.
We use the drag-and-drop utility provided by the @dnd-kit library and update the state and backend whenever the order of items is changed through drag-and-drop interactions.
The primary role of the DragDropList component is to display the list of items using Material-UI components, ensuring a visually appealing and consistent UI.
Step 7: Update the App and main components
Update the src/App.jsx component to include the DragDropList.jsx component.
// src/App.jsx import { CssBaseline, Container, Typography } from "@mui/material"; import DragDropList from "./components/DragDropList"; const App = () => { return ( <Container> <CssBaseline /> <Typography variant="h4" component="h1" gutterBottom> Drag and Drop List </Typography> <DragDropList /> </Container> ); }; export default App;
Update the src/main.jsx component:
// src/main.jsx import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' import { CssBaseline, ThemeProvider, createTheme } from '@mui/material' const theme = createTheme(); ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <ThemeProvider theme={theme}> <CssBaseline /> <App /> </ThemeProvider> </React.StrictMode>, )
Save the file and start the Vite development server using the command below:
npm run dev
Make sure that you are already running the backend json server.
Now, go to this URL: http://localhost:5173/