| name | dnd-kit-implementation |
| description | Guide for implementing sortable and droppable components using dnd-kit library. Use this skill when building React applications that require drag-and-drop functionality with both container reordering (useSortable) and item dropping (useDroppable) capabilities, such as Kanban boards, file management systems, or playlist editors. |
dnd-kit Implementation Guide
Overview
This skill provides patterns for implementing drag-and-drop functionality using dnd-kit library that supports both sortable containers and droppable targets simultaneously.
Core Concept
The key to combining useSortable and useDroppable is conditional logic based on drag context:
- When dragging items → containers act as drop-only targets
- When dragging containers → containers act as sortable elements
This is achieved by detecting what's currently being dragged and enabling only the appropriate functionality.
Implementation Pattern
Container Component Structure
const SortableDroppableContainer = ({ container }) => {
const {
attributes,
listeners,
setNodeRef: setSortableRef,
transform,
transition,
isDragging,
} = useSortable({ id: container.id });
const { setNodeRef: setDroppableRef, isOver } = useDroppable({
id: container.id,
});
const { active } = useDndContext();
const isItem = active?.id?.toString().startsWith('item-');
const isContainer = active?.id?.toString().startsWith('container-');
const setNodeRef = isItem ? setDroppableRef : setSortableRef;
return (
<div ref={setNodeRef} {...(isContainer ? attributes : {})} style={style}>
<div {...(isContainer ? listeners : {})}>{/* Drag handle */}</div>
{/* Container content */}
</div>
);
};
Key Implementation Points
- Dual Hook Usage: Use both
useSortable and useDroppable in the same component
- Context Detection: Use
useDndContext() to check what's being dragged
- Conditional Refs: Apply the appropriate ref based on drag state
- ID Naming Convention: Use prefixes like
container-* and item-* to distinguish types
ID Naming Convention
const containerId = `container-${id}`;
const itemId = `item-${id}`;
This convention makes conditional logic clean and maintainable.
State Management Critical Point
When updating container items, always create new objects:
containers.items.push(newItem);
setContainers(prev => prev.map(c =>
c.id === targetId
? { ...c, items: [...c.items, newItem] }
: c
));
Common Use Cases
- Kanban Boards: Reorder columns and move cards between columns
- File Management: Reorganize folders and move files into folders
- Playlist Editors: Reorder playlists and add songs to playlists
- Task Managers: Reorder task lists and move tasks between lists
References
For detailed implementation examples and code patterns, see: