Guides

Migration guide

A comprehensive guide to migrate from `@dnd-kit/core` to `@dnd-kit/react`

1

Update dependencies

Update your package.json and install the new packages:

  "dependencies": {
    "@dnd-kit/core": "^x.x.x",      
    "@dnd-kit/sortable": "^x.x.x",  
    "@dnd-kit/utilities": "^x.x.x", 
    "@dnd-kit/react": "^x.x.x",     
    "@dnd-kit/helpers": "^x.x.x",   
  }

After updating your package.json, install the new dependencies with your package manager of choice.

2

Migrate context provider

Update your context provider. The new DragDropProvider replaces the legacy DndContext:

import {DndContext} from '@dnd-kit/core';

function App() {
  return (
    <DndContext
      onDragStart={({active}) => {
        console.log(`Started dragging ${active.id}`);
      }}
      onDragEnd={({active, over}) => {
        if (over) {
          console.log(`Dropped ${active.id} over ${over.id}`);
        }
      }}
      onDragCancel={({active}) => {
        console.log(`Cancelled dragging ${active.id}`);
      }}
    >
      <YourComponents />
    </DndContext>
  );
}
import {DragDropProvider} from '@dnd-kit/react';

function App() {
  return (
    <DragDropProvider
      onDragStart={(event, manager) => {
        const {operation} = event;
        console.log(`Started dragging ${operation.source.id}`);
      }}
      onDragEnd={(event, manager) => {
        const {operation, canceled} = event;
        const {source, target} = operation;

        if (canceled) {
          // Replaces onDragCancel
          console.log(`Cancelled dragging ${source.id}`);
          return;
        }

        if (target) {
          console.log(`Dropped ${source.id} over ${target.id}`);
          // Access rich data
          console.log('Source data:', source.data);
          console.log('Drop position:', operation.position.current);
        }
      }}
    >
      <YourComponents />
    </DragDropProvider>
  );
}

The new provider gives you access to the manager instance in event handlers, enabling more advanced control over the drag and drop system.

3

Update draggable components

Update your draggable components using the new useDraggable hook:

function DraggableItem({id}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform
  } = useDraggable({
    id
  });

  return (
    <div
      ref={setNodeRef}
      {...listeners}
      {...attributes}
      style={{
        transform: CSS.Transform.toString(transform)
      }}
    >
      Item {id}
    </div>
  );
}
function DraggableItem({id}) {
  const draggable = useDraggable({
    id,
  });

  return (
    <div
      ref={draggable.ref}
      className={isDragging ? 'dragging' : ''}
    >
      Item {id}
    </div>
  );
}
4

Update droppable components

Update your droppable components using the new useDroppable hook:

function Dropzone() {
  const {setNodeRef, isOver} = useDroppable({
    id: 'drop-zone'
  });

  return (
    <div
      ref={setNodeRef}
      style={{
        background: isOver ? 'lightblue' : 'white'
      }}
    >
      Drop here
    </div>
  );
}
function Dropzone() {
  const droppable = useDroppable({
    id: 'drop-zone'
  });

  return (
    <div
      ref={droppable.ref}
      style={{
        background: droppable.isDropTarget ? 'green' : 'white'
      }}
    >
      Drop here
    </div>
  );
}
5

Update drag overlay

The DragOverlay component has been simplified:

function App() {
  const [activeId, setActiveId] = useState(null);

  return (
    <DndContext
      onDragStart={() => setActiveId('item')}
      onDragEnd={() => setActiveId(null)}
    >
      <Draggable id="item" />
      <DragOverlay>
        {activeId ? <Item id={activeId} /> : null}
      </DragOverlay>
    </DndContext>
  );
}
function App() {
  return (
    <DragDropProvider>
      <Draggable id="item" />
      <DragOverlay>
        {source => (
          <Item id={source.id} />
        )}
      </DragOverlay>
    </DragDropProvider>
  );
}

Only render the DragOverlay component once per DragDropProvider. The hooks have no effect when used within the overlay.

6

Migrate sortable components

Update your sortable components using the new useSortable hook:

import {useSortable} from '@dnd-kit/sortable';

function SortableItem({id, index}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition
  } = useSortable({
    id,
    index
  });

  return (
    <div
      ref={setNodeRef}
      {...attributes}
      {...listeners}
      style={{
        transform: CSS.Transform.toString(transform),
        transition
      }}
    >
      Item {id}
    </div>
  );
}
import {useSortable} from '@dnd-kit/react/sortable';

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

  return (
    <div
      ref={sortable.ref}
      className={sortable.isDragging ? 'dragging' : undefined}
    >
      Item {id}
    </div>
  );
}

Use the array manipulation helpers from @dnd-kit/helpers to handle reordering.

API reference

A quick lookup for common legacy APIs and their new equivalents, organized by category.

Context & Provider

Legacy (@dnd-kit/core)New (@dnd-kit/react)
DndContextDragDropProvider

Events

LegacyNew
activeevent.operation.source
overevent.operation.target
active.idevent.operation.source.id
onDragCancelCheck event.canceled inside onDragEnd

Sensors

The legacy MouseSensor and TouchSensor have been merged into a single PointerSensor that handles mouse, touch, and pen input via the native Pointer Events API. You can still apply different behavior per input type by passing a function to activationConstraints and branching on event.pointerType ('mouse', 'touch', or 'pen'):

import {DragDropProvider} from '@dnd-kit/react';
import {PointerSensor, PointerActivationConstraints} from '@dnd-kit/dom';

<DragDropProvider
  sensors={(defaults) => [
    ...defaults.filter((sensor) => sensor !== PointerSensor),
    PointerSensor.configure({
      activationConstraints(event, source) {
        if (event.pointerType === 'touch') {
          return [
            new PointerActivationConstraints.Delay({value: 500, tolerance: {x: 5, y: 5}}),
          ];
        }
        return [new PointerActivationConstraints.Distance({value: 8})];
      },
    }),
  ]}
>
  {/* ... */}
</DragDropProvider>

If you don’t configure it, the default already differentiates pointer types — see the PointerSensor docs for the full default behavior.

Legacy (@dnd-kit/core)New (@dnd-kit/dom)
useSensor / useSensorsBuilt-in by default. Customize via the sensors prop on DragDropProvider
MouseSensorPointerSensor from @dnd-kit/dom
TouchSensorPointerSensor from @dnd-kit/dom
PointerSensorPointerSensor from @dnd-kit/dom
KeyboardSensorKeyboardSensor from @dnd-kit/dom

Collision detection

LegacyNew
collisionDetection prop on DndContextcollisionDetector option on useDroppable / useSortable
pointerWithinpointerIntersection from @dnd-kit/collision
closestCenterclosestCenter from @dnd-kit/collision
closestCornersclosestCorners from @dnd-kit/collision

Modifiers

See the React modifiers guide for usage examples.

LegacyNew
restrictToParentElementRestrictToElement from @dnd-kit/dom/modifiers
restrictToWindowEdgesRestrictToWindow from @dnd-kit/dom/modifiers
restrictToVerticalAxisRestrictToVerticalAxis from @dnd-kit/abstract/modifiers
restrictToHorizontalAxisRestrictToHorizontalAxis from @dnd-kit/abstract/modifiers
createSnapModifierSnapModifier from @dnd-kit/abstract/modifiers

Sortable

SortableContext is no longer needed. Sortable items register and coordinate with the DragDropProvider automatically when you use useSortable — there’s no wrapping context to configure. Use the type and accept options on useSortable to control which items can be sorted together.

See the sortable state management guide for usage examples.

LegacyNew
SortableContextNo longer needed — see note above
arrayMovemove from @dnd-kit/helpers
verticalListSortingStrategyNot needed — handled automatically
horizontalListSortingStrategyNot needed — handled automatically
rectSortingStrategyNot needed — handled automatically

Next steps