# Make empty dnd-kit columns explicit drop targets

> Sortable cards are drop targets, but an empty lane has no card to collide with. Register the column itself and handle container IDs explicitly.

Source: https://saascode.ai/inside/dndkit-empty-column-not-droppable · Published: 2026-08-21 · Section: academy

---
Your Kanban board works until somebody clears a column. After that, cards dragged into the empty lane snap back to where they started.

That exact asymmetry appeared in a live pipeline board. Seed data hid it: every development column contained a card, so there was always a sortable item for collision detection to find.

In dnd-kit, `useSortable()` composes draggable and droppable behavior for each item. `SortableContext` describes the item order, but it does not turn an empty container into a physical drop target. With no items, `event.over` has nothing to reference unless the lane itself is registered.

## Register the container

Give every lane a stable ID and attach `useDroppable()` to the element that should accept the card:

```tsx
function KanbanColumn({ columnId, cardIds, children }: Props) {
  const { setNodeRef, isOver } = useDroppable({
    id: `column:${columnId}`,
    data: { type: 'column', columnId },
  })

  return (
    <section
      ref={setNodeRef}
      data-over={isOver || undefined}
      className="min-h-32"
    >
      <SortableContext items={cardIds}>
        {children}
      </SortableContext>
    </section>
  )
}
```

Namespacing container IDs prevents a column ID from colliding with a card ID. The `data` payload also lets the drag handler distinguish “over a card” from “over a column” without guessing from string membership.

## Resolve both collision shapes

When the pointer is over a card, derive its current container and insertion index. When it is over a column, use that column directly and choose the product's intended position—often the end of the list. Handle `over === null` as a cancelled drop rather than mutating state.

For multiple containers, dnd-kit's legacy sortable guide explicitly shows a droppable zone around each `SortableContext` so an emptied column remains reachable. Collision strategy still matters: `closestCenter` or `closestCorners` is often more forgiving than rectangle intersection for narrow lanes.

## Test the state your fixtures avoid

Start with all columns empty. Add one card, move it into another empty column, move it back, and clear the destination again. Repeat with a keyboard sensor, not only a pointer.

Dragging also needs an accessible alternative. WCAG 2.2 requires functionality that uses dragging to be achievable with a single pointer without dragging unless the movement is essential. A “Move to…” menu or equivalent control is often clearer than forcing every user through drag-and-drop.

The snap-back bug was not in the card. It was in the absence of a target. Empty states are real states, and interactive geometry has to exist even when the data array does not.
