Sortable drag-drop list in React using HTML Drag Drop API
4 min read
β’
Jan 9, 2026
Hi there! Welcome to my blog, this is Alan again π.
Today, Iβm gonna share with you how to create a Sortable drag-drop list just using HTML Drag Drop native API.
Sound interesting?
Letβs get started!
First, we need to initialize a project, use the below command to create a new one. For convenience, the command will generate a source code including NextJS, Charka UI, and many other useful for starting a project right away without extra steps.
Or you can use another tool such as create-react-app.
1npx @tinychange/new-next sortable-dragdrop-list
After the initialization, create src/pages/index.tsx for home page, and then add the below code to create an UI of a list of names
12import { Box } from '@chakra-ui/react'3import { useState } from 'react'45const DragItem = ({ name }: { name: string }) => {6 return (7 <Box8 sx={{9 padding: 2,10 borderRadius: 2,11 mb: 2,12 border: '1px solid rgba(0,0,0,0.1)',13 _hover: {14 cursor: 'pointer',15 },16 }}17 >18 {name}19 </Box>20 )21}2223export default function Home() {24 const [data, setData] = useState([25 'Alice',26 'Bob',27 'Charlie',28 'Diana',29 'Ethan',30 'Fiona',31 'George',32 'Hannah',33 'Isaac',34 'Julia',35 ])3637 return (38 <Box39 sx={{40 border: '1px solid rgba(0,0,0,0.3)',41 borderRadius: 4,42 width: 300,43 margin: 'auto',44 padding: 4,45 }}46 >47 {data.map((item) => (48 <DragItem key={item} name={item} />49 ))}50 </Box>51 )52}
There is no complicated logic here, just a simple UI.
Press enter or click to view image in full size

Now weβre going into the main part, drag-drop.
Make the Name item draggable and when an item is being dragged, it should leave an empty space.
Update the DragItem component as below:
1import { DragEventHandler, useState } from 'react'23const DragItem = ({ name }: { name: string }) => {4 const [isDragging, setIsDragging] = useState(false)56 return (7 <Box8 draggable9 onDrag={() => setIsDragging(true)}10 onDragEnd={() => setIsDragging(false)}11 onDragOver={(evt) => evt.preventDefault()}12 sx={{13 padding: 2,14 borderRadius: 2,15 mb: 2,16 border: '1px solid rgba(0,0,0,0.1)',17 opacity: isDragging ? 0 : 1,18 _hover: {19 cursor: 'pointer',20 },21 }}22 >23 {name}24 </Box>25 )26}27
Press enter or click to view image in full size

Awesome!
Next, we need to move other items when the item being dragged goes over any other. The expected behavior will be:
- Letβs say you drag Alice to Bob
- Bob item will move to Alice position and leave an empty space at Bob position for Alice
- You continue holding and dragging Alice to Charlie, the Charlie moves up and leave an empty space for Alice
- And if you still drag Alice and jump to Fiona, all of the items prior Fiona and Fiona will move up and leave an empty space for Alice after Fiona
Add the code below to component Home:
12 const refDragIndex = useRef<number>()34 const onDragStart = (dragIndex: number) => {5 refDragIndex.current = dragIndex6 }78// add index and onDragStart to DragItem9 {data.map((n, i) => (10 <DragItem11 onDragStart={onDragStart}12 index={i}13 key={n}14 name={n}15 />16 ))}
And DragItem component
1const DragItem = ({2 name,3 index,4 onDragStart,5}: {6 name: string7 index: number8 onDragStart: (dragIndex: number) => void9})10 ...11}1213// add this after onDragEnd14onDragStart={() => onDragStart(index)}
Okay, nothing happens on the UI yet because we have just stored the position index of item being dragged. We still have one more function to make a change happen on the UI β changing position of dragging item and the target.
1// Add this function to Home component2const onMove = (dropIndex: number) => {3 const dragIndex = refDragIndex.current456 setData((prevList) => {7 if (dragIndex !== undefined) {8 // move dragging item to new position9 const dragItem = prevList.splice(dragIndex, 1)10 prevList.splice(dropIndex, 0, ...dragItem)11 }12 return [...prevList]13 })1415 // reset drag item position to new index16 refDragIndex.current = dropIndex17}181920// pass onMove to DragItem21<DragItem22 onMove={onMove}23 onDragStart={onDragStart}24 index={i}25 key={item}26 name={item}27/>
And update the DragItem as below:
12// add to props of DragItem component3 onMove: (dropIndex: number) => void45// add this to DragItem component and6const _onDragEnter: DragEventHandler<HTMLElement> = (evt) => {7 evt.preventDefault()8 onMove(index)9}1011// and defined onDragEnter for the <Box> in DragItem12onDragEnter={_onDragEnter}13
And here we are:
Press enter or click to view image in full size

Woohoo!
No more worries about drag-drop list even your work requires to use native api.
Here is the source code for your reference: https://github.com/alanng2050/blog-demo-sortable-drag-drop-list
Good luck!
Thanks for reading!