The helpers package provides utility functions for common drag and drop operations, particularly for managing array and list mutations.
move
Moves items in an array or grouped record based on drag and drop events, intelligently handling both simple arrays and grouped data structures.
function move<T extends Items | Record<UniqueIdentifier, Items>>(
items: T,
event: DragDropEvent
): T
Parameters
items
Items | Record<UniqueIdentifier, Items>
required
The items to reorder. Can be:
- An array of identifiers:
UniqueIdentifier[]
- An array of objects with
id properties: {id: UniqueIdentifier}[]
- A record of groups, each containing an array of items:
Record<UniqueIdentifier, Items>
A dragover or dragend event from the DragDropManager containing source and target information
Returns
A new array or record with items moved to their new positions. Returns the original items unchanged if the operation is invalid or canceled.
Usage Example
import {move} from '@dnd-kit/helpers';
import {DragDropManager} from '@dnd-kit/dom';
// Simple array of IDs
const items = ['item-1', 'item-2', 'item-3'];
const manager = new DragDropManager();
manager.monitor.addEventListener('dragend', (event) => {
const newItems = move(items, event);
console.log(newItems);
// ['item-2', 'item-1', 'item-3'] - if item-2 was moved before item-1
});
// Array of objects
const tasks = [
{id: 'task-1', title: 'First task'},
{id: 'task-2', title: 'Second task'},
{id: 'task-3', title: 'Third task'},
];
manager.monitor.addEventListener('dragend', (event) => {
const newTasks = move(tasks, event);
// Objects are moved to new positions
});
// Grouped data (e.g., Kanban board)
const columns = {
'todo': ['task-1', 'task-2'],
'in-progress': ['task-3'],
'done': ['task-4', 'task-5'],
};
manager.monitor.addEventListener('dragend', (event) => {
const newColumns = move(columns, event);
// Handles both within-group reordering and cross-group transfers
});
Behavior
- Simple Arrays: Moves the source item to the target position
- Grouped Records: Handles both reordering within a group and moving items between groups
- Optimistic Updates: Reconciles optimistic UI updates with actual data positions
- Invalid Operations: Returns original items unchanged and calls
event.preventDefault() if:
- Source or target is missing
- Operation was canceled
- Indices are out of bounds
- Source equals target with no actual movement
swap
Swaps two items in an array or grouped record based on drag and drop events.
function swap<T extends Items | Record<UniqueIdentifier, Items>>(
items: T,
event: DragDropEvent
): T
Parameters
items
Items | Record<UniqueIdentifier, Items>
required
The items to reorder. Can be:
- An array of identifiers:
UniqueIdentifier[]
- An array of objects with
id properties: {id: UniqueIdentifier}[]
- A record of groups, each containing an array of items:
Record<UniqueIdentifier, Items>
A dragover or dragend event from the DragDropManager containing source and target information
Returns
A new array or record with items swapped. Returns the original items unchanged if the operation is invalid or canceled.
Usage Example
import {swap} from '@dnd-kit/helpers';
import {DragDropManager} from '@dnd-kit/dom';
const items = ['A', 'B', 'C', 'D'];
const manager = new DragDropManager();
manager.monitor.addEventListener('dragend', (event) => {
const newItems = swap(items, event);
// If 'A' was dragged onto 'C': ['C', 'B', 'A', 'D']
// The items at source and target positions are swapped
});
Difference from move
- move: Shifts items between positions (inserting at target)
- swap: Exchanges the items at source and target positions
// Before: ['A', 'B', 'C', 'D']
// Drag 'A' (index 0) to 'C' (index 2)
move(items, event) // ['B', 'C', 'A', 'D'] - A inserted at C's position
swap(items, event) // ['C', 'B', 'A', 'D'] - A and C swapped positions
arrayMove
Moves an array item to a different position. Returns a new array with the item moved to the new position.
function arrayMove<T extends any[]>(
array: T,
from: number,
to: number
): T
Parameters
Returns
A new array with the item moved. Returns the original array if from === to.
Usage Example
import {arrayMove} from '@dnd-kit/helpers';
const items = ['A', 'B', 'C', 'D', 'E'];
const result = arrayMove(items, 0, 3);
// ['B', 'C', 'D', 'A', 'E']
const result2 = arrayMove(items, 4, 1);
// ['A', 'E', 'B', 'C', 'D']
arraySwap
Swaps two items in an array. Returns a new array with the items swapped.
function arraySwap<T extends any[]>(
array: T,
from: number,
to: number
): T
Parameters
Returns
A new array with the items swapped. Returns the original array if from === to.
Usage Example
import {arraySwap} from '@dnd-kit/helpers';
const items = ['A', 'B', 'C', 'D', 'E'];
const result = arraySwap(items, 0, 3);
// ['D', 'B', 'C', 'A', 'E']
const result2 = arraySwap(items, 1, 4);
// ['A', 'E', 'C', 'D', 'B']
Types
UniqueIdentifier
type UniqueIdentifier = string | number;
Items
type Items = UniqueIdentifier[] | {id: UniqueIdentifier}[];
DragDropEvent
type DragDropEvent =
| DragDropEventMap['dragover']
| DragDropEventMap['dragend'];
interface DragDropEventMap {
dragover: {
operation: {
source: Draggable;
target: Droppable;
canceled: boolean;
};
preventDefault(): void;
};
dragend: {
operation: {
source: Draggable;
target: Droppable;
canceled: boolean;
};
preventDefault(): void;
};
}
Best Practices
Using with dragover events
For real-time reordering during drag:
const [items, setItems] = useState(['A', 'B', 'C']);
manager.monitor.addEventListener('dragover', (event) => {
setItems(move(items, event));
});
Using with dragend events
For committing changes only after drop:
const [items, setItems] = useState(['A', 'B', 'C']);
manager.monitor.addEventListener('dragend', (event) => {
const newItems = move(items, event);
setItems(newItems);
// Save to backend, etc.
});
Handling grouped data
const [columns, setColumns] = useState({
todo: [{id: '1', title: 'Task 1'}],
done: [{id: '2', title: 'Task 2'}],
});
manager.monitor.addEventListener('dragend', (event) => {
const newColumns = move(columns, event);
setColumns(newColumns);
});
Preventing unwanted mutations
The helper functions return new arrays/objects, so your original data remains unchanged:
const original = ['A', 'B', 'C'];
const modified = arrayMove(original, 0, 2);
console.log(original); // ['A', 'B', 'C'] - unchanged
console.log(modified); // ['B', 'C', 'A'] - new array