A modular toolkit for building drag & drop interfaces for React
Crash Course!
"A modular, lightweight, performant, accessible and extensible drag & drop toolkit for React."
No | Item | Description |
---|---|---|
1. | Total Commits | 259 |
2. | Last Commit | 20 days ago |
3. | Opened Issues | 74 out of 155 |
4. | Opened Pull Requests | 11 out of 122 closed |
5. | Total Stars | 2.7k |
6. | Total Fork | 96 |
7. | First Public Release | 3rd Jan 2021 (v1.0.0) |
8. | Latest version | v3.1.1 |
No | Item | react-dnd | react-beautiful-dnd (rbd) | dnd-kit |
---|---|---|---|---|
1. | Owner | atlassian | shopify | |
2. | Total Commits | 2,170 | 785 | 259 |
3. | Last Commit | 3 days ago | 2 months ago | 20 days ago |
4. | Opened Issues | 243 out of 1,233 | 399 out of 985 | 74 out of 155 |
5. | Opened Pull Requests | 8 out of 1,810 | 66 out of 852 | 11 out of 122 |
6. | Total Stars | 16.2k | 24.5k | 2.7k |
7. | Total Fork | 1.7k | 1.8k | 96 |
8. | First Public Release | 19 May 2015 (v1.0.0) | 11 Aug 2017 (v1.0.1) | 3rd Jan 2021 (v1.0.0) |
9. | Latest version | v14.0.3 | v13.1.0 | v3.1.1 |
No | Item | react-dnd | rbd | dnd-kit |
---|---|---|---|---|
1. | Use Cases | ❤️ Anything | List, Kaban | ❤️ Anything |
2. | Learning Curve | High | ❤️ Low | ❤️ Low |
3. | Flexibility | ❤️ High | Low | ❤️ High |
4. | Sensor/Backend | HTML5 | ❤️ Pointer, Mouse, Touch, Keyboard | ❤️ Pointer, Mouse, Touch, Keyboard |
5. | Performance | Low | ❤️ High | ❤️ High |
6. | Accessibility | Low | ❤️ High | ❤️ High |
7. | Modularity - Styling/ Animation - Collision Detection - Overlay/Constrains - Drag Handles - Presets |
Low | Medium | ❤️ High |
import React from 'react';
import {DndContext} from '@dnd-kit/core';
import {Draggable} from './Draggable';
import {Droppable} from './Droppable';
function App() {
return (
<DndContext>
<Draggable />
<Droppable />
</DndContext>
)
}
In order for your Droppable and Draggable components to interact with each other, you'll need to make sure that the part of your React tree that uses them is nested within a parent <DndContext> component.
import React from 'react';
import {useDroppable} from '@dnd-kit/core';
function Droppable(props) {
const {isOver, setNodeRef} = useDroppable({
id: 'droppable',
});
const style = {
color: isOver ? 'green' : undefined,
};
return (
<div ref={setNodeRef} style={style}>
{props.children}
</div>
);
}
Let's set up your first Droppable component. When a draggable element is moved over your droppable element, the isOver property will become true.
Change the bg to green when isOver
import React from 'react';
import {useDraggable} from '@dnd-kit/core';
function Draggable(props) {
const {attributes, listeners, setNodeRef, transform} = useDraggable({
id: 'draggable',
});
const style = transform ? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
} : undefined;
return (
<button ref={setNodeRef} style={style} {...listeners} {...attributes}>
{props.children}
</button>
);
}
Next, let's take a look at implementing our first Draggable component. After a draggable item is picked up, the transform property will be populated with the translate coordinates you'll need to move the item on the screen.
Next we need to define what happens when draggable is dropped into droppable.
import React from 'react';
import {DndContext} from '@dnd-kit/core';
import {Droppable} from './Droppable';
import {Draggable} from './Draggable';
function App() {
const [isDropped, setIsDropped] = useState(false);
const draggableMarkup = (
<Draggable>Drag me</Draggable>
);
return (
<DndContext onDragEnd={handleDragEnd}>
{!isDropped ? draggableMarkup : null}
<Droppable>
{isDropped ? draggableMarkup : 'Drop here'}
</Droppable>
</DndContext>
);
function handleDragEnd(event) {
if (event.over && event.over.id === 'droppable') {
setIsDropped(true);
}
}
}
Define a onDragEnd handler to update isDropped state.
if isDropped is true, show Draggable item inside the Droppable area.
import React from 'react';
import {DndContext} from '@dnd-kit/core';
import {Droppable} from './Droppable';
import {Draggable} from './Draggable';
function App() {
const containers = ['A', 'B', 'C'];
const [parent, setParent] = useState(null);
const draggableMarkup = (
<Draggable id="draggable">Drag me</Draggable>
);
return (
<DndContext onDragEnd={handleDragEnd}>
{parent === null ? draggableMarkup : null}
{containers.map((id) => (
// We updated the Droppable component so it would accept an `id`
// prop and pass it to `useDroppable`
<Droppable key={id} id={id}>
{parent === id ? draggableMarkup : 'Drop here'}
</Droppable>
))}
</DndContext>
);
function handleDragEnd(event) {
const {over} = event;
// If the item is dropped over a container, set it as the parent
// otherwise reset the parent to `null`
setParent(over ? over.id : null);
}
};
Multiple Droppables Example
interface Props {
announcements?: Announcements;
autoScroll?: boolean;
cancelDrop?: CancelDrop;
children?: React.ReactNode;
collisionDetection?: CollisionDetection;
layoutMeasuring?: Partial<LayoutMeasuring>;
modifiers?: Modifiers;
screenReaderInstructions?: ScreenReaderInstructions;
sensors?: SensorDescriptor<any>[];
onDragStart?(event: DragStartEvent): void;
onDragMove?(event: DragMoveEvent): void;
onDragOver?(event: DragOverEvent): void;
onDragEnd?(event: DragEndEvent): void;
onDragCancel?(): void;
}
dnd-kit provides all the building blocks to build a sortable interface from scratch but there's already a preset for that.
more presets for common uses cases are coming in the future.