Skip to content
5 changes: 4 additions & 1 deletion apps/docs/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@
"pages": [
"react/guides/migration",
"react/guides/multiple-sortable-lists",
"react/guides/sortable-state-management"
"react/guides/sortable-state-management",
"react/guides/collision-detection",
"react/guides/modifiers",
"react/guides/feedback"
]
}
]
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs/extend/sensors/keyboard-sensor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The Keyboard sensor enables keyboard-based drag and drop interactions. It is ena

```ts
import {DragDropManager} from '@dnd-kit/dom';
import {KeyboardSensor} from '@dnd-kit/dom/sensors';
import {KeyboardSensor} from '@dnd-kit/dom';

const manager = new DragDropManager({
sensors: [
Expand Down
23 changes: 23 additions & 0 deletions apps/docs/docs/extend/sensors/pointer-sensor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,29 @@ The Pointer sensor handles these events:

The sensor automatically binds listeners across all same-origin documents, enabling drag operations across same-origin iframes.

## Touch and mobile

The Pointer sensor handles touch input on mobile devices by default — no additional setup is required.

On touch devices, dragging activates after a **250ms delay** with **5px movement tolerance**. This prevents accidental drags when scrolling. You can customize these constraints for your use case:

```ts
PointerSensor.configure({
activationConstraints(event) {
if (event.pointerType === 'touch') {
return [
new PointerActivationConstraints.Delay({
value: 150, // Shorter delay for faster touch response
tolerance: 10, // More tolerance for finger movement
}),
];
}

return undefined; // Use defaults for mouse/pen
},
});
```

### Best Practices

1. Use appropriate constraints for different input types:
Expand Down
34 changes: 34 additions & 0 deletions apps/docs/docs/react/components/drag-drop-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,40 @@ function App() {
Called when collisions are detected. Call `event.preventDefault()` to prevent automatic target selection.
</ParamField>

#### Event object structure

All event handlers receive an event object and the `manager` instance. The most commonly used event, `onDragEnd`, has this shape:

```ts
onDragEnd={(event, manager) => {
event.operation.source // The dragged element (Draggable), or null
event.operation.target // The drop target (Droppable), or null
event.operation.source.id // Unique identifier of the dragged element
event.operation.target?.id // Unique identifier of the drop target
event.operation.position // { current: {x, y}, initial: {x, y} }
event.operation.status // Status of the drag operation
event.canceled // true if the drag was cancelled (e.g. Escape key)
event.nativeEvent // The underlying browser event, if available
}}
```

For sortable elements, use the [`isSortable`](/react/guides/sortable-state-management#type-guards) type guard to access sortable-specific properties:

```tsx
import {isSortable} from '@dnd-kit/react/sortable';

onDragEnd={(event) => {
const {source} = event.operation;

if (isSortable(source)) {
source.index // Current position
source.initialIndex // Position when the drag started
source.group // Current group (for multi-list)
source.initialGroup // Group when the drag started
}
}}
```

### Configuration

<ParamField path="sensors" type="Sensor[] | (defaults: Sensor[]) => Sensor[]">
Expand Down
148 changes: 148 additions & 0 deletions apps/docs/docs/react/guides/collision-detection.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
title: 'Collision detection'
metaTitle: 'Collision Detection - React | dnd kit'
description: 'Customize how draggable elements detect collisions with droppable targets in React. Includes closestCenter, closestCorners, pointerIntersection, and custom detectors.'
icon: 'arrows-to-circle'
---

## Overview

When you drag something over a droppable target, dnd kit runs a **collision detection algorithm** to decide which target is currently being hovered. The default works well for most lists, but you can customize it per droppable when you need different behavior — for example, card stacks, grids, or nested containers.

Built-in detectors are exported from the `@dnd-kit/collision` package. You don't need to install it explicitly — it's already a transitive dependency of `@dnd-kit/react`.

## Built-in detectors

<Note>
All built-in detectors are exported from `@dnd-kit/collision`. If you don't pass a `collisionDetector`, dnd kit uses [`defaultCollisionDetection`](#default) automatically — you only need this guide when you want to override it.
</Note>

### `defaultCollisionDetection`

The default. Runs `pointerIntersection` first, and falls back to `shapeIntersection` when the pointer isn't inside any droppable.

### `pointerIntersection`

High precision — a collision is only detected when the pointer is **inside** the droppable's bounding rectangle. Good for precise drop zones where you want the user to clearly be "over" a target.

### `shapeIntersection`

Returns the droppable with the **greatest overlap area** with the dragged element's shape. Good for large containers where any visual overlap should count as a collision. Ties are broken by distance to the pointer.

<Frame>
<img src="/images/droppable/shape-intersection.svg" alt="Shape intersection: the dragged element overlaps droppable rectangles and the one with the greatest overlap area is selected" />
</Frame>

### `closestCenter`

Picks the droppable whose **center point** is closest to the dragged element's center. Ideal for card stacking or grids where items snap to the nearest slot.

<Frame>
<img src="/images/droppable/closest-center.svg" alt="Closest center: a line from the dragged element's center to the closest droppable's center" />
</Frame>

### `closestCorners`

Picks the droppable with the smallest **average corner-to-corner distance**. More forgiving than `closestCenter` at the edges of a list — useful for vertical sortable lists where you want items to pick up a neighbor as soon as the corners approach.

<Frame>
<img src="/images/legacy/closest-corners.png" alt="Closest corners: lines drawn between the four corners of the dragged element and the corresponding corners of each droppable" />
</Frame>

### `pointerDistance`

Returns the droppable whose center is closest to the **pointer coordinates** (not the dragged element). Useful when you want drop detection to follow the cursor rather than the dragged element.

### `directionBiased`

Only detects collisions in the **direction the user is dragging** (up, down, left, right). Useful when you want items to only be swapped when the user drags *toward* them — prevents jitter when hovering near the edge between two targets.

## Configuring a detector

The `collisionDetector` option is set **per droppable**. Pass any built-in detector (or a custom one) to [`useDroppable`](/react/hooks/use-droppable) or [`useSortable`](/react/hooks/use-sortable):

```tsx
import {useDroppable} from '@dnd-kit/react';
import {closestCenter} from '@dnd-kit/collision';

function DropTarget({id}) {
const {ref, isDropTarget} = useDroppable({
id,
collisionDetector: closestCenter,
});

return <div ref={ref}>{isDropTarget ? 'Drop here' : 'Empty'}</div>;
}
```

The same option works on `useSortable`:

```tsx
import {useSortable} from '@dnd-kit/react/sortable';
import {closestCorners} from '@dnd-kit/collision';

function SortableItem({id, index}) {
const sortable = useSortable({
id,
index,
collisionDetector: closestCorners,
});

return <div ref={sortable.ref}>Item {id}</div>;
}
```

<Tip>
Because `collisionDetector` is configured per droppable rather than globally, different targets on the same page can use different algorithms. For example, a free-form canvas area might use `pointerIntersection` while a sortable list next to it uses `closestCenter`.
</Tip>

## Collision priority

When multiple droppables overlap — for example, a sortable card inside a droppable column — you can bias which one "wins" using `collisionPriority`. Higher values take precedence.

```tsx
import {useDroppable} from '@dnd-kit/react';
import {CollisionPriority} from '@dnd-kit/abstract';

function Column({id, children}) {
const {ref} = useDroppable({
id,
collisionPriority: CollisionPriority.Low, // Cards inside will win by default
});

return <div ref={ref}>{children}</div>;
}
```

The `CollisionPriority` enum is exported from `@dnd-kit/abstract` and provides named levels: `Lowest`, `Low`, `Normal`, `High`, `Highest`. You can also pass a plain number.

## Writing a custom detector

A collision detector is a function that receives the drag operation and a droppable, and returns a collision (or `null` if there's no collision). The return `value` is a score — higher scores win when multiple droppables collide.

```tsx
import type {CollisionDetector} from '@dnd-kit/abstract';
import {CollisionPriority, CollisionType} from '@dnd-kit/abstract';

const myDetector: CollisionDetector = ({dragOperation, droppable}) => {
if (!droppable.shape) return null;

// Your logic here — return null for no collision, or an object:
return {
id: droppable.id,
value: 1, // Higher values win
type: CollisionType.Collision,
priority: CollisionPriority.Normal,
};
};
```

Pass it to any droppable:

```tsx
useDroppable({id, collisionDetector: myDetector});
```

For reference implementations, read the source of the built-in detectors in [`@dnd-kit/collision`](https://github.com/clauderic/dnd-kit/tree/main/packages/collision/src/algorithms).

If you're coming from `@dnd-kit/core`, see the [Migration guide](/react/guides/migration#collision-detection) for legacy API mappings.
129 changes: 129 additions & 0 deletions apps/docs/docs/react/guides/feedback.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
title: 'Feedback'
metaTitle: 'Feedback Plugin - React | dnd kit'
description: 'Configure drag feedback, clone overlays, drop animations, and troubleshoot common issues like duplicate items in React.'
icon: 'clone'
---

## Overview

The [Feedback](/extend/plugins/feedback) plugin manages visual feedback during drag operations. It is included by default and handles element promotion to the browser's top layer and drop animations.

This guide covers common React patterns and troubleshooting.

## Configuring feedback globally

To configure feedback for all draggable elements, pass a `plugins` function to [`DragDropProvider`](/react/components/drag-drop-provider):

```tsx
import {DragDropProvider} from '@dnd-kit/react';
import {Feedback} from '@dnd-kit/dom';

function App() {
return (
<DragDropProvider
plugins={(defaults) => [
...defaults,
Feedback.configure({dropAnimation: null}),
]}
>
{/* All draggable elements will have no drop animation */}
</DragDropProvider>
);
}
```

<Warning>
Use the function form `(defaults) => [...]` to extend the default plugins rather than replacing them. Passing a plain array replaces all default plugins, which may disable expected behavior like auto-scrolling and accessibility.
</Warning>

## Per-draggable feedback

Configure feedback on individual elements via the `plugins` option in [`useDraggable`](/react/hooks/use-draggable) or [`useSortable`](/react/hooks/use-sortable):

```tsx
import {useDraggable} from '@dnd-kit/react';
import {Feedback} from '@dnd-kit/dom';

function CloneDraggable({id}: {id: string}) {
const {ref} = useDraggable({
id,
plugins: [
Feedback.configure({
feedback: 'clone',
}),
],
});

return <div ref={ref}>Drag me (clone stays behind)</div>;
}
```

## Feedback modes

The `feedback` option controls how the element behaves during drag:

| Mode | Behavior |
|------|----------|
| `'default'` | The element is promoted to the top layer and moves with the pointer |
| `'clone'` | A clone of the element stays in its original position while the original moves |
| `'move'` | The element moves without top layer promotion or a placeholder |
| `'none'` | No visual feedback from the Feedback plugin. Use this when rendering a custom [`DragOverlay`](/react/components/drag-overlay) |

## Drop animations

### Disabling the drop animation

```tsx
Feedback.configure({dropAnimation: null})
```

### Customizing the drop animation

```tsx
Feedback.configure({
dropAnimation: {
duration: 300, // milliseconds (default: 250)
easing: 'ease', // CSS easing (default: 'ease')
},
})
```

### Custom animation function

For full control, provide a function that returns a `Promise`:

```tsx
Feedback.configure({
dropAnimation: async ({element, translate}) => {
await element.animate(
[{transform: `translate3d(${translate.x}px, ${translate.y}px, 0)`}, {transform: 'translate3d(0, 0, 0)'}],
{duration: 200, easing: 'ease-out'}
).finished;
},
})
```

## Troubleshooting

### Duplicate items after data refetch

When using sortable lists with React Query or other data fetching libraries, you may see duplicate items after a refetch. This happens because the [OptimisticSortingPlugin](/concepts/sortable#optimistic-sorting) has moved DOM elements during the drag, and the refetched data renders new elements in the updated positions.

See [Integration with external state](/react/guides/sortable-state-management#integration-with-external-state) for how to solve this by deferring state sync until the drag is complete.

### Double animation or snap on drop

If items snap into position twice after dropping, your state update is likely conflicting with the drop animation. The Feedback plugin animates the element back, then your state update triggers a re-render that moves it again.

To fix this, disable the drop animation:

```tsx
Feedback.configure({dropAnimation: null})
```

Or ensure your state update matches the final position by using the [`move`](/react/guides/sortable-state-management) helper from `@dnd-kit/helpers` in `onDragEnd`, which correctly reconciles the optimistic position with your state.

<Info>
For the complete API reference including `rootElement` and `keyboardTransition` options, see the [core Feedback plugin documentation](/extend/plugins/feedback).
</Info>
Loading
Loading