AlgoInsight

Q&A

How to drag and drop using React

By AlgoInsight Team2026-06-07frontendreactjavascript

Overview

Drag and drop lets users grab an element and move it - reordering lists, moving cards between columns (kanban), uploading files. In React you wire the browser's drag events to state updates. The native API works for basic needs but is fiddly and weak on touch/accessibility, so production apps usually reach for a library.

graph LR
    A[onDragStart: record dragged item] --> B[onDragOver: preventDefault to allow drop]
    B --> C[onDrop: read data, update state]
    C --> D[React re-renders new order]
    style C fill:#1e1b4b,color:#fff

Native HTML5 Drag and Drop

Make an element draggable and handle the events:

import { useState } from "react";

function DragList() {
  const [items, setItems] = useState(["A", "B", "C", "D"]);
  const [dragIndex, setDragIndex] = useState(null);

  const onDrop = (dropIndex) => {
    if (dragIndex === null || dragIndex === dropIndex) return;
    const next = [...items];
    const [moved] = next.splice(dragIndex, 1);   // remove dragged
    next.splice(dropIndex, 0, moved);            // insert at target
    setItems(next);
    setDragIndex(null);
  };

  return (
    <ul>
      {items.map((item, i) => (
        <li
          key={item}
          draggable
          onDragStart={() => setDragIndex(i)}
          onDragOver={(e) => e.preventDefault()}   // REQUIRED to allow a drop
          onDrop={() => onDrop(i)}
          style={{ padding: 8, border: "1px solid #ccc", cursor: "grab" }}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}

The non-obvious essential: you must call e.preventDefault() in onDragOver, otherwise onDrop never fires (by default elements aren't valid drop targets).

Passing Data via dataTransfer

The native API also has a dataTransfer object to carry payload between drag and drop:

onDragStart={(e) => e.dataTransfer.setData("text/plain", item)}
onDrop={(e) => {
  const data = e.dataTransfer.getData("text/plain");
  // use data
}}

This is especially useful for dragging between different drop zones or from outside (e.g. files via e.dataTransfer.files).

Using a Library (recommended for real apps)

@dnd-kit (modern, accessible, the current go-to)

import { DndContext, closestCenter } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
// Wrap items in DndContext + SortableContext; useSortable gives drag props per item.
// Handle onDragEnd to reorder your state array via arrayMove(...)

@dnd-kit provides keyboard support, touch sensors, collision detection, and accessibility out of the box.

Library comparison

Library Best for Notes
@dnd-kit General, accessible DnD Modern, modular, keyboard + touch
@hello-pangea/dnd Vertical/horizontal lists Maintained fork of react-beautiful-dnd
react-dnd Complex custom interactions Powerful, HTML5 backend, steeper API

Accessibility and Touch

The native HTML5 API has poor touch support and no keyboard story. If users on mobile or assistive tech matter (they usually do), a library like @dnd-kit that supports pointer/touch sensors and keyboard dragging is the responsible choice.

Why This Matters in Interviews

Front-end interviews use this to test event handling and state-driven reordering. The must-know native detail is calling e.preventDefault() in onDragOver so onDrop fires; the must-know judgment is that the native API is weak on touch/accessibility, so production reorder/kanban features use a library (@dnd-kit is the current standard). Reordering via immutable array ops (splice on a copy) demonstrates correct React state handling.

Common Pitfalls

References

More answers