Documentation Index
Fetch the complete documentation index at: https://mintlify.com/clauderic/dnd-kit/llms.txt
Use this file to discover all available pages before exploring further.
Custom Collision Detection
Collision detection algorithms determine which droppable area a draggable item should interact with during a drag operation. dnd-kit provides several built-in algorithms and allows you to create custom ones for specialized use cases.
Understanding Collision Detection
A collision detector is a function that receives information about the current drag operation and a droppable target, and returns a collision result with a value indicating the strength of the collision.
type CollisionDetector = (input: {
dragOperation: DragOperation;
droppable: Droppable;
}) => CollisionResult | null;
type CollisionResult = {
id: string;
value: number; // Higher values = stronger collision
type: CollisionType;
priority: CollisionPriority;
};
Built-in Algorithms
dnd-kit includes several collision detection algorithms:
closestCenter
Detects the droppable whose center is closest to the drag source. This is ideal for general-purpose drag and drop.
packages/collision/src/algorithms/closestCenter.ts
import {closestCenter} from '@dnd-kit/collision';
const manager = new DragDropManager({
collisionDetector: closestCenter,
});
Implementation detail: It calculates the distance using Point.distance(droppable.shape.center, shape?.current.center ?? position.current) and returns 1 / distance as the collision value.
pointerIntersection
High-precision algorithm that only detects collisions when the pointer is directly over a droppable element.
packages/collision/src/algorithms/pointerIntersection.ts
import {pointerIntersection} from '@dnd-kit/collision';
const manager = new DragDropManager({
collisionDetector: pointerIntersection,
});
This algorithm checks if droppable.shape.containsPoint(pointerCoordinates) and has high priority (CollisionPriority.High).
directionBiased
Detects collisions based on movement direction, perfect for sortable lists where you only want to detect items in the direction you’re moving.
packages/collision/src/algorithms/directionBiased.ts
import {directionBiased} from '@dnd-kit/collision';
const manager = new DragDropManager({
collisionDetector: directionBiased,
});
Custom Collision Detection Examples
Example 1: Zone-Based Collision Detection
Create a collision detector that prioritizes specific zones on your canvas:
import {CollisionDetector, CollisionPriority, CollisionType} from '@dnd-kit/abstract';
import {Point} from '@dnd-kit/geometry';
const zonePriorities = {
'zone-1': 3,
'zone-2': 2,
'zone-3': 1,
};
const zoneBased: CollisionDetector = ({dragOperation, droppable}) => {
if (!droppable.shape) return null;
const {shape, position} = dragOperation;
const dragCenter = shape?.current.center ?? position.current;
// Check if the drag position is within the droppable bounds
if (!droppable.shape.containsPoint(dragCenter)) {
return null;
}
// Get zone priority from droppable data
const zonePriority = droppable.data.get('zone')
? zonePriorities[droppable.data.get('zone')] ?? 1
: 1;
// Calculate distance for tie-breaking
const distance = Point.distance(droppable.shape.center, dragCenter);
const distanceValue = distance === 0 ? 1 : 1 / distance;
// Combine zone priority with distance
const value = zonePriority * 1000 + distanceValue;
return {
id: droppable.id,
value,
type: CollisionType.Collision,
priority: CollisionPriority.High,
};
};
// Usage
const manager = new DragDropManager({
collisionDetector: zoneBased,
});
const droppable1 = manager.registry.droppables.register(element1, {
data: new Map([['zone', 'zone-1']]),
});
Example 2: Threshold-Based Collision
Only detect collisions when the draggable overlaps a droppable by a certain percentage:
import {CollisionDetector, CollisionPriority, CollisionType} from '@dnd-kit/abstract';
import {Rectangle} from '@dnd-kit/geometry';
function createThresholdCollisionDetector(threshold: number = 0.5): CollisionDetector {
return ({dragOperation, droppable}) => {
const {shape} = dragOperation;
if (!shape || !droppable.shape) return null;
const dragRect = shape.current.boundingRectangle;
const dropRect = droppable.shape.boundingRectangle;
// Calculate intersection area
const intersectionLeft = Math.max(dragRect.left, dropRect.left);
const intersectionTop = Math.max(dragRect.top, dropRect.top);
const intersectionRight = Math.min(dragRect.right, dropRect.right);
const intersectionBottom = Math.min(dragRect.bottom, dropRect.bottom);
if (intersectionRight <= intersectionLeft || intersectionBottom <= intersectionTop) {
return null; // No intersection
}
const intersectionArea =
(intersectionRight - intersectionLeft) * (intersectionBottom - intersectionTop);
const dragArea = dragRect.width * dragRect.height;
const overlapRatio = intersectionArea / dragArea;
// Only return collision if overlap exceeds threshold
if (overlapRatio < threshold) {
return null;
}
return {
id: droppable.id,
value: overlapRatio,
type: CollisionType.ShapeIntersection,
priority: CollisionPriority.Normal,
};
};
}
// Usage: Require 75% overlap before detecting collision
const manager = new DragDropManager({
collisionDetector: createThresholdCollisionDetector(0.75),
});
Example 3: Combining Multiple Algorithms
Chain multiple collision detection strategies with fallback logic:
import {
CollisionDetector,
CollisionPriority,
CollisionType,
} from '@dnd-kit/abstract';
import {pointerIntersection, closestCenter} from '@dnd-kit/collision';
function combineCollisionDetectors(
...detectors: CollisionDetector[]
): CollisionDetector {
return (input) => {
for (const detector of detectors) {
const result = detector(input);
if (result) return result;
}
return null;
};
}
// Try pointer intersection first, fall back to closest center
const hybridDetector = combineCollisionDetectors(
pointerIntersection,
closestCenter
);
const manager = new DragDropManager({
collisionDetector: hybridDetector,
});
Example 4: Grid-Snapping Collision Detection
Detect the nearest grid cell for precise placement:
import {CollisionDetector, CollisionPriority, CollisionType} from '@dnd-kit/abstract';
import {Point} from '@dnd-kit/geometry';
function createGridCollisionDetector(
gridSize: {x: number; y: number}
): CollisionDetector {
return ({dragOperation, droppable}) => {
if (!droppable.shape) return null;
const {position, shape} = dragOperation;
const dragPoint = shape?.current.center ?? position.current;
// Get the grid cell coordinates from droppable data
const gridX = droppable.data.get('gridX');
const gridY = droppable.data.get('gridY');
if (typeof gridX !== 'number' || typeof gridY !== 'number') {
return null;
}
// Calculate the center point of this grid cell
const cellCenter = {
x: gridX * gridSize.x + gridSize.x / 2,
y: gridY * gridSize.y + gridSize.y / 2,
};
// Calculate distance from drag point to cell center
const distance = Point.distance(cellCenter, dragPoint);
// Only consider cells within a reasonable range
const maxDistance = Math.sqrt(gridSize.x ** 2 + gridSize.y ** 2);
if (distance > maxDistance) {
return null;
}
const value = 1 / (distance + 1);
return {
id: droppable.id,
value,
type: CollisionType.Collision,
priority: CollisionPriority.Normal,
};
};
}
// Usage
const manager = new DragDropManager({
collisionDetector: createGridCollisionDetector({x: 100, y: 100}),
});
// Register grid cells as droppables
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const cell = document.querySelector(`[data-grid="${row}-${col}"]`);
manager.registry.droppables.register(cell, {
data: new Map([
['gridX', col],
['gridY', row],
]),
});
}
}
Best Practices
-
Return null for non-collisions: Always return
null when there’s no collision instead of a result with a value of 0.
-
Use appropriate priorities: Set
CollisionPriority.High for precise collisions (like pointer intersection) and CollisionPriority.Normal for proximity-based detection.
-
Normalize collision values: Keep collision values in a reasonable range. The built-in algorithms use
1 / distance which provides good resolution.
-
Guard against null shapes: Always check if
droppable.shape exists before accessing its properties.
-
Consider performance: Collision detection runs frequently during dragging. Keep calculations lightweight.
Collision Types
The library defines several collision types that affect how collisions are processed:
CollisionType.Collision - Standard collision detection
CollisionType.PointerIntersection - High-precision pointer-based collision
CollisionType.ShapeIntersection - Shape-based overlap detection
Higher priority collisions take precedence when multiple droppables are detected.