> ## 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

> Advanced examples of custom collision detection algorithms for specialized drag and drop scenarios

# 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.

```typescript theme={null}
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.

```typescript packages/collision/src/algorithms/closestCenter.ts theme={null}
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.

```typescript packages/collision/src/algorithms/pointerIntersection.ts theme={null}
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.

```typescript packages/collision/src/algorithms/directionBiased.ts theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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

1. **Return null for non-collisions**: Always return `null` when there's no collision instead of a result with a value of 0.

2. **Use appropriate priorities**: Set `CollisionPriority.High` for precise collisions (like pointer intersection) and `CollisionPriority.Normal` for proximity-based detection.

3. **Normalize collision values**: Keep collision values in a reasonable range. The built-in algorithms use `1 / distance` which provides good resolution.

4. **Guard against null shapes**: Always check if `droppable.shape` exists before accessing its properties.

5. **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.
