Back to Machine Coding
Machine Coding
easy
mid

How would you build a drag and drop feature in a web app?

Two approaches: the native HTML Drag and Drop API (draggable, onDragStart/Over/Drop) or pointer-events math. State tracks the dragged item and drop target; reorder the list immutably on drop. Mention accessibility (keyboard reordering) and that a library (dnd-kit) is the production choice.

5 min read·~25 min to think through

Drag & drop has two implementation paths — know both, and know that production usually means a library.

Approach 1: native HTML Drag and Drop API

jsx
function DragList({ items, setItems }) {
  const dragItem = useRef(null);     // index being dragged
  const dragOverItem = useRef(null); // index being hovered

  const handleDrop = () => {
    const list = [...items];
    const [moved] = list.splice(dragItem.current, 1);  // remove dragged
    list.splice(dragOverItem.current, 0, moved);       // insert at target
    setItems(list);
    dragItem.current = dragOverItem.current = null;
  };

  return items.map((item, i) => (
    <div
      key={item.id}
      draggable
      onDragStart={() => (dragItem.current = i)}
      onDragEnter={() => (dragOverItem.current = i)}
      onDragOver={(e) => e.preventDefault()}   // REQUIRED to allow a drop
      onDragEnd={handleDrop}
    >
      {item.label}
    </div>
  ));
}

Key gotchas: you must e.preventDefault() on onDragOver or onDrop never fires; data can be carried via dataTransfer; the native drag image and styling are limited.

Approach 2: pointer events

Listen to pointerdown/move/up, track the pointer position, and compute which slot the dragged element is over by comparing bounding rects. More code, but full control — custom drag previews, smooth animations, touch support, constraints. This is what real libraries do under the hood.

State model

  • The dragged item (id/index) and the current drop target.
  • On drop, reorder the list immutably (splice on a copy) — never mutate state directly.
  • Use stable item ids as keys, not indices — reordering with index keys corrupts the UI.

What separates a strong answer

  • Accessibility — native DnD is not keyboard-accessible. A complete solution adds keyboard reordering (focus an item, arrow keys to move, space to drop) and aria-live announcements. This is the most-missed requirement.
  • Touch support — the native API is poor on touch; pointer events handle it properly.
  • Visual feedback — drop-indicator line, placeholder gap, drag preview.
  • Use a library in productiondnd-kit (modern, accessible, performant) or react-beautiful-dnd. Hand-rolling is fine for an interview to show understanding, but say you'd reach for dnd-kit for real.
  • Performance — for long lists, avoid re-rendering every item on each dragOver; throttle and memoize rows.

The framing

"Two paths: the native HTML Drag and Drop API — draggable, onDragStart/Enter/Over/Drop, with the classic gotcha that you must preventDefault on dragOver — or pointer events with bounding-rect math for full control over previews, touch, and animation. Either way, state is the dragged item plus the drop target, and on drop I reorder the list immutably with stable id keys. The senior points: native DnD isn't keyboard-accessible so I'd add keyboard reordering and aria-live, and for production I'd use dnd-kit rather than hand-roll it."

Follow-up questions

  • Why must you call preventDefault on onDragOver?
  • How would you make drag & drop keyboard-accessible?
  • Native Drag and Drop API vs pointer events — trade-offs?
  • How do you keep a long draggable list performant?

Common mistakes

  • Forgetting e.preventDefault() on onDragOver — onDrop never fires.
  • Using array indices as keys, corrupting the list on reorder.
  • Mutating the array instead of reordering immutably.
  • Ignoring accessibility — native DnD has no keyboard support.
  • Ignoring touch devices, where the native API is unreliable.

Performance considerations

  • dragOver/pointermove fire rapidly — throttle handlers and memoize list rows so the whole list doesn't re-render per event. For very long lists, combine with virtualization.

Edge cases

  • Dropping an item onto itself or outside any target.
  • Reordering to the first or last position.
  • Touch devices and the native API's poor support there.
  • Long lists re-rendering on every dragOver event.
  • Nested drop zones.

Real-world examples

  • Reorderable task lists (Trello/Jira boards), file upload zones, kanban columns.
  • dnd-kit powering accessible drag-and-drop in production apps.

Senior engineer discussion

Seniors know both implementation paths and their trade-offs, nail the preventDefault and stable-key gotchas, proactively raise accessibility (keyboard reordering, aria-live) and touch support, address performance on long lists, and recommend dnd-kit for production.

Related questions