javascript 63 lines · 8 steps

Drag-to-reorder lists in React

A React component that lets users reorder list items with the native HTML drag-and-drop API and three pieces of state.

Explained by highlit
1import { useState } from 'react';
2 
3export function ReorderableList({ initialItems }) {
4 const [items, setItems] = useState(initialItems);
5 const [draggingId, setDraggingId] = useState(null);
6 const [overId, setOverId] = useState(null);
7 
8 const handleDragStart = (event, id) => {
9 setDraggingId(id);
10 event.dataTransfer.effectAllowed = 'move';
11 event.dataTransfer.setData('text/plain', String(id));
12 };
13 
14 const handleDragOver = (event, id) => {
15 event.preventDefault();
16 event.dataTransfer.dropEffect = 'move';
17 if (id !== overId) setOverId(id);
18 };
19 
20 const handleDrop = (event, targetId) => {
21 event.preventDefault();
22 if (draggingId === null || draggingId === targetId) return;
23 
24 setItems((prev) => {
25 const next = [...prev];
26 const from = next.findIndex((item) => item.id === draggingId);
27 const to = next.findIndex((item) => item.id === targetId);
28 const [moved] = next.splice(from, 1);
29 next.splice(to, 0, moved);
30 return next;
31 });
32 };
33 
34 const handleDragEnd = () => {
35 setDraggingId(null);
36 setOverId(null);
37 };
38 
39 return (
40 <ul className="reorderable-list">
41 {items.map((item) => (
42 <li
43 key={item.id}
44 draggable
45 onDragStart={(e) => handleDragStart(e, item.id)}
46 onDragOver={(e) => handleDragOver(e, item.id)}
47 onDrop={(e) => handleDrop(e, item.id)}
48 onDragEnd={handleDragEnd}
49 className={[
50 'reorderable-item',
51 draggingId === item.id && 'is-dragging',
52 overId === item.id && draggingId !== item.id && 'is-over',
53 ]
54 .filter(Boolean)
55 .join(' ')}
56 >
57 <span className="drag-handle" aria-hidden="true"></span>
58 {item.label}
59 </li>
60 ))}
61 </ul>
62 );
63}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Tracking the dragged item and hover target in state lets you compute both the reorder and the visual feedback.
  2. 2Reordering immutably with a copied array plus splice keeps React's state updates predictable.
  3. 3The native HTML drag-and-drop API needs preventDefault on dragover and drop for a drop to register.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Drag-to-reorder lists in React — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code