Overview
This guide walks you through creating your first drag and drop interface with dnd-kit. You’ll learn the fundamental concepts by building a simple draggable item, then progress to creating a sortable list.
This guide uses React examples. For other frameworks, the concepts are identical—only the syntax differs slightly.
Prerequisites
Before starting, make sure you have:
Basic framework knowledge
Familiarity with React hooks (or equivalent in your framework) is helpful.
Your first draggable
Let’s start with the simplest possible example: a single draggable element.
Set up the provider
Wrap your application (or the part that needs drag and drop) with DragDropProvider: import { DragDropProvider } from '@dnd-kit/react' ;
function App () {
return (
< DragDropProvider >
{ /* Your draggable elements will go here */ }
</ DragDropProvider >
);
}
export default App ;
The DragDropProvider manages the drag and drop state and coordinates all draggable and droppable elements.
Create a draggable component
Use the useDraggable hook to make an element draggable: import { useState } from 'react' ;
import { useDraggable } from '@dnd-kit/react' ;
export function Draggable () {
const [ element , setElement ] = useState < Element | null >( null );
const { isDragging } = useDraggable ({
id: 'draggable-1' ,
element ,
});
return (
< div
ref = { setElement }
style = { {
padding: '20px' ,
border: '2px solid #666' ,
borderRadius: '8px' ,
backgroundColor: isDragging ? '#e3f2fd' : '#fff' ,
cursor: isDragging ? 'grabbing' : 'grab' ,
opacity: isDragging ? 0.5 : 1 ,
} }
>
Drag me!
</ div >
);
}
Key points:
Each draggable needs a unique id
Pass the element reference via the element prop
Use isDragging to provide visual feedback
Use your draggable
Import and render your draggable component: import { DragDropProvider } from '@dnd-kit/react' ;
import { Draggable } from './Draggable' ;
function App () {
return (
< DragDropProvider >
< div style = { { padding: '50px' } } >
< Draggable />
</ div >
</ DragDropProvider >
);
}
export default App ;
You should now be able to drag the element around! The element will follow your cursor and return to its original position when released.
Adding a drop zone
Now let’s add a droppable area where you can drop the draggable element.
Create a droppable component
Use the useDroppable hook: import { useState } from 'react' ;
import { useDroppable } from '@dnd-kit/react' ;
export function Droppable ({ children } : { children : React . ReactNode }) {
const [ element , setElement ] = useState < Element | null >( null );
const { isDropTarget } = useDroppable ({
id: 'droppable-1' ,
element ,
});
return (
< div
ref = { setElement }
style = { {
minHeight: '200px' ,
padding: '20px' ,
border: '2px dashed #999' ,
borderRadius: '8px' ,
backgroundColor: isDropTarget ? '#e8f5e9' : '#f5f5f5' ,
transition: 'background-color 0.2s' ,
} }
>
{ children }
</ div >
);
}
The isDropTarget property indicates when a draggable is hovering over this droppable.
Handle drop events
Add event handlers to track when items are dropped: import { DragDropProvider } from '@dnd-kit/react' ;
import type { DragEndEvent } from '@dnd-kit/react' ;
import { Draggable } from './Draggable' ;
import { Droppable } from './Droppable' ;
function App () {
const handleDragEnd = ( event : DragEndEvent ) => {
const { source , target } = event . operation ;
if ( target ) {
console . log ( `Dropped ${ source . id } on ${ target . id } ` );
}
};
return (
< DragDropProvider onDragEnd = { handleDragEnd } >
< div style = { { padding: '50px' , display: 'flex' , gap: '20px' } } >
< div >
< h3 > Draggable </ h3 >
< Draggable />
</ div >
< div style = { { flex: 1 } } >
< h3 > Drop Zone </ h3 >
< Droppable >
Drop here!
</ Droppable >
</ div >
</ div >
</ DragDropProvider >
);
}
export default App ;
Building a sortable list
Now let’s create something more practical: a sortable list where you can reorder items by dragging.
Install the helpers package
The helpers package provides utilities for common operations like reordering: npm install @dnd-kit/helpers @dnd-kit/collision
Create a sortable item component
Use the useSortable hook for items in a sortable list: import { useState , memo } from 'react' ;
import { useSortable } from '@dnd-kit/react/sortable' ;
import { directionBiased } from '@dnd-kit/collision' ;
interface Props {
id : string | number ;
index : number ;
}
export const SortableItem = memo ( function SortableItem ({ id , index } : Props ) {
const [ element , setElement ] = useState < Element | null >( null );
const { isDragging } = useSortable ({
id ,
index ,
element ,
collisionDetector: directionBiased ,
});
return (
< div
ref = { setElement }
style = { {
padding: '16px 20px' ,
marginBottom: '8px' ,
backgroundColor: '#fff' ,
border: '1px solid #ddd' ,
borderRadius: '8px' ,
cursor: 'grab' ,
opacity: isDragging ? 0.5 : 1 ,
boxShadow: isDragging
? '0 10px 20px rgba(0,0,0,0.2)'
: '0 1px 3px rgba(0,0,0,0.1)' ,
transition: 'box-shadow 0.2s' ,
} }
>
{ id }
</ div >
);
});
Key differences from basic draggables:
Uses useSortable instead of useDraggable
Requires both id and index props
Uses directionBiased collision detection optimized for lists
Create the sortable list
Build the list with state management: import { useState } from 'react' ;
import { DragDropProvider } from '@dnd-kit/react' ;
import type { DragEndEvent } from '@dnd-kit/react' ;
import { move } from '@dnd-kit/helpers' ;
import { SortableItem } from './SortableItem' ;
export function SortableList () {
const [ items , setItems ] = useState ([
'Item 1' ,
'Item 2' ,
'Item 3' ,
'Item 4' ,
'Item 5' ,
]);
const handleDragEnd = ( event : DragEndEvent ) => {
setItems (( currentItems ) => move ( currentItems , event ));
};
return (
< DragDropProvider onDragEnd = { handleDragEnd } >
< div style = { { maxWidth: '400px' , margin: '0 auto' , padding: '20px' } } >
< h2 > Sortable List </ h2 >
< div >
{ items . map (( item , index ) => (
< SortableItem key = { item } id = { item } index = { index } />
)) }
</ div >
</ div >
</ DragDropProvider >
);
}
The move helper automatically calculates the new array order based on the drag event.
You now have a fully functional sortable list! Try dragging items to reorder them.
Adding visual feedback
Enhance the user experience with a drag overlay that follows the cursor:
import { useState } from 'react' ;
import { DragDropProvider , DragOverlay } from '@dnd-kit/react' ;
import type { DragStartEvent , DragEndEvent } from '@dnd-kit/react' ;
import { move } from '@dnd-kit/helpers' ;
import { SortableItem } from './SortableItem' ;
export function SortableList () {
const [ items , setItems ] = useState ([ 'Item 1' , 'Item 2' , 'Item 3' , 'Item 4' , 'Item 5' ]);
const [ activeId , setActiveId ] = useState < string | null >( null );
const handleDragStart = ( event : DragStartEvent ) => {
setActiveId ( event . operation . source . id as string );
};
const handleDragEnd = ( event : DragEndEvent ) => {
setItems (( currentItems ) => move ( currentItems , event ));
setActiveId ( null );
};
return (
< DragDropProvider
onDragStart = { handleDragStart }
onDragEnd = { handleDragEnd }
>
< div style = { { maxWidth: '400px' , margin: '0 auto' , padding: '20px' } } >
< h2 > Sortable List with Overlay </ h2 >
< div >
{ items . map (( item , index ) => (
< SortableItem key = { item } id = { item } index = { index } />
)) }
</ div >
</ div >
< DragOverlay >
{ activeId ? (
< div style = { {
padding: '16px 20px' ,
backgroundColor: '#fff' ,
border: '1px solid #ddd' ,
borderRadius: '8px' ,
boxShadow: '0 10px 30px rgba(0,0,0,0.3)' ,
} } >
{ activeId }
</ div >
) : null }
</ DragOverlay >
</ DragDropProvider >
);
}
The DragOverlay renders a custom element that follows the cursor during drag operations.
Advanced features
Now that you have the basics down, explore these advanced features:
Drag handles Add a specific handle that must be grabbed to initiate dragging: const handleRef = useRef < HTMLButtonElement >( null );
const { isDragging } = useDraggable ({
id: 'item' ,
element ,
handle: handleRef ,
});
Modifiers Restrict movement to an axis or snap to grid: import { RestrictToVerticalAxis } from '@dnd-kit/abstract/modifiers' ;
< DragDropProvider modifiers = { [ RestrictToVerticalAxis ] } >
{ /* ... */ }
</ DragDropProvider >
Multiple containers Create complex layouts with multiple droppable containers and transfer items between them.
Custom sensors Configure activation constraints and custom input methods: import { PointerSensor } from '@dnd-kit/react' ;
const sensors = [
PointerSensor . configure ({
activationConstraint: {
distance: 10 , // 10px movement required
},
}),
];
Event lifecycle
Understand the drag and drop event lifecycle:
beforedragstart
Fired before a drag operation begins. You can prevent the drag by calling event.preventDefault().
dragstart
Fired when a drag operation starts. Perfect for tracking analytics or updating UI state.
dragmove
Fired continuously as the draggable moves. Use sparingly for performance.
dragover
Fired when a draggable moves over a droppable. Ideal for visual feedback.
dragend
Fired when a drag operation completes, whether successful or cancelled. Update your data here.
< DragDropProvider
onBeforeDragStart = { ( event ) => {
console . log ( 'About to start dragging' , event . operation . source . id );
} }
onDragStart = { ( event ) => {
console . log ( 'Started dragging' , event . operation . source . id );
} }
onDragOver = { ( event ) => {
console . log ( 'Dragging over' , event . operation . target ?. id );
} }
onDragEnd = { ( event ) => {
console . log ( 'Finished dragging' , event . operation );
} }
>
{ /* ... */ }
</ DragDropProvider >
Common patterns
Optimistic updates
Update the UI immediately during drag for a snappier feel:
function SortableList () {
const [ items , setItems ] = useState ([ 'A' , 'B' , 'C' ]);
return (
< DragDropProvider
onDragOver = { ( event ) => {
// Update immediately as user drags
setItems (( items ) => move ( items , event ));
} }
onDragEnd = { ( event ) => {
// Final update (in case dragover was missed)
setItems (( items ) => move ( items , event ));
} }
>
{ /* ... */ }
</ DragDropProvider >
);
}
Conditional dropping
Control what can be dropped where:
const { isDropTarget } = useDroppable ({
id: 'container-1' ,
element ,
accept: [ 'type-a' , 'type-b' ], // Only accept certain types
});
Disabled items
Temporarily disable dragging:
const { isDragging } = useDraggable ({
id: 'item-1' ,
element ,
disabled: isProcessing , // Disable during async operations
});
Next steps
You’ve built your first drag and drop interfaces! Continue learning:
API reference Explore the complete API documentation
Examples Browse more complex examples and patterns
Accessibility Learn about keyboard navigation and screen reader support
Performance Optimize your drag and drop for large lists