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.
npx @tinychange/new-next sortable-dragdrop-listAfter 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
import { Box } from '@chakra-ui/react'
import { useState } from 'react'
const DragItem = ({ name }: { name: string }) => {
return (
<Box
sx={{
padding: 2,
borderRadius: 2,
mb: 2,
border: '1px solid rgba(0,0,0,0.1)',
_hover: {
cursor: 'pointer',
},
}}
>
{name}
</Box>
)
}
export default function Home() {
const [data, setData] = useState([
'Alice',
'Bob',
'Charlie',
'Diana',
'Ethan',
'Fiona',
'George',
'Hannah',
'Isaac',
'Julia',
])
return (
<Box
sx={{
border: '1px solid rgba(0,0,0,0.3)',
borderRadius: 4,
width: 300,
margin: 'auto',
padding: 4,
}}
>
{data.map((item) => (
<DragItem key={item} name={item} />
))}
</Box>
)
}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:
import { DragEventHandler, useState } from 'react'
const DragItem = ({ name }: { name: string }) => {
const [isDragging, setIsDragging] = useState(false)
return (
<Box
draggable
onDrag={() => setIsDragging(true)}
onDragEnd={() => setIsDragging(false)}
onDragOver={(evt) => evt.preventDefault()}
sx={{
padding: 2,
borderRadius: 2,
mb: 2,
border: '1px solid rgba(0,0,0,0.1)',
opacity: isDragging ? 0 : 1,
_hover: {
cursor: 'pointer',
},
}}
>
{name}
</Box>
)
}
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:
const refDragIndex = useRef<number>()
const onDragStart = (dragIndex: number) => {
refDragIndex.current = dragIndex
}
// add index and onDragStart to DragItem
{data.map((n, i) => (
<DragItem
onDragStart={onDragStart}
index={i}
key={n}
name={n}
/>
))}And DragItem component
const DragItem = ({
name,
index,
onDragStart,
}: {
name: string
index: number
onDragStart: (dragIndex: number) => void
})
...
}
// add this after onDragEnd
onDragStart={() => 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.
// Add this function to Home component
const onMove = (dropIndex: number) => {
const dragIndex = refDragIndex.current
setData((prevList) => {
if (dragIndex !== undefined) {
// move dragging item to new position
const dragItem = prevList.splice(dragIndex, 1)
prevList.splice(dropIndex, 0, ...dragItem)
}
return [...prevList]
})
// reset drag item position to new index
refDragIndex.current = dropIndex
}
// pass onMove to DragItem
<DragItem
onMove={onMove}
onDragStart={onDragStart}
index={i}
key={item}
name={item}
/>And update the DragItem as below:
// add to props of DragItem component
onMove: (dropIndex: number) => void
// add this to DragItem component and
const _onDragEnter: DragEventHandler<HTMLElement> = (evt) => {
evt.preventDefault()
onMove(index)
}
// and defined onDragEnter for the <Box> in DragItem
onDragEnter={_onDragEnter}
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!