Drag and Drop in React 2026: dnd-kit vs react-beautiful-dnd
dnd-kit or react-beautiful-dnd? In 2026 one is actively maintained and one isn't. Here's the honest breakdown to help you pick the right library.
The State of React Drag and Drop in 2026
If you've shipped a Kanban board, a sortable list, or a file upload zone in the last three years, you've probably asked this question: dnd-kit or react-beautiful-dnd? Short answer — dnd-kit. Longer answer follows.
react-beautiful-dnd was the gold standard back in 2019. Atlassian built it for Jira, it looked gorgeous out of the box, and the API felt like it was designed by humans. That said, Atlassian officially stopped active development on it in 2022, and by 2026 the issues tab is a graveyard of unanswered React 18 compatibility bugs and concurrent mode complaints.
dnd-kit, by contrast, shipped its v6 in late 2024 with full React 19 support, a proper accessibility tree, and a modular architecture that doesn't shove 40 kb of code into your bundle if you only need sortable lists. It's now the default recommendation in most serious React projects.
Worth noting: neither library is "the only option." If you're building something wildly custom — think a node-based visual editor or a 2D canvas — you might want to roll a lower-level solution with the Pointer Events API directly. But for 95% of product work, you want dnd-kit.
Why react-beautiful-dnd Is Effectively Dead
Look, I don't enjoy writing obituaries for libraries I've used happily. But you need to know this before you start a new project. react-beautiful-dnd hasn't had a meaningful release since version 13.1.1 in 2022, and its peer dependency still lists react: ^16.8.0 || ^17.0.0. That should tell you everything.
The real pain shows up with React 18's concurrent rendering. react-beautiful-dnd uses ReactDOM.findDOMNode() internally, which was deprecated in React 18 and removed in React 19. If you're on a modern stack, you'll hit warnings in dev mode and subtle breakage in production. Not ideal.
In practice, you'll also notice the performance ceiling fast. Drop a list of 500+ items and the re-render cascade becomes visible at 60fps. dnd-kit's virtual list support handles that cleanly because it was designed with performance as a first-class concern, not an afterthought.
Quick aside: if you're already maintaining a codebase that uses react-beautiful-dnd and a migration isn't justified right now, that's fine. Just don't start new features with it.
Getting Started with dnd-kit
Installation is one line. Nothing fancy here.
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilitiesThe mental model is three layers: DndContext wraps everything and owns the drag state. SortableContext tells dnd-kit which items are sortable and in what order. Individual useSortable hooks attach to each item. That separation is clean and it means you can mix sortable and non-sortable drop zones in the same context without fighting the library.
Here's a minimal sortable list — the kind you'd use for a Kanban column or a settings panel with reorderable rows:
import { useState } from 'react';
import {
DndContext,
closestCenter,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
function SortableItem({ id }) {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
padding: '12px 16px',
marginBottom: '8px',
background: '#1e1e2e',
borderRadius: '8px',
cursor: 'grab',
userSelect: 'none',
};
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
{id}
</div>
);
}
export function SortableList() {
const [items, setItems] = useState(['Task A', 'Task B', 'Task C', 'Task D']);
const sensors = useSensors(useSensor(PointerSensor, {
activationConstraint: { distance: 8 }, // px before drag starts
}));
function handleDragEnd(event) {
const { active, over } = event;
if (active.id !== over?.id) {
setItems((items) => {
const oldIndex = items.indexOf(active.id);
const newIndex = items.indexOf(over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
return (
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
<SortableContext items={items} strategy={verticalListSortingStrategy}>
{items.map((id) => <SortableItem key={id} id={id} />)}
</SortableContext>
</DndContext>
);
}That activationConstraint: { distance: 8 } is worth explaining. Without it, a single click fires the drag handlers and your click events stop working on interactive list items like buttons or inputs. 8px is the sweet spot — enough to distinguish an intentional drag from an accidental tap. You'll thank yourself later.
Sensors, Collision Detection, and the Bits That Actually Trip People Up
dnd-kit ships three sensors: PointerSensor, KeyboardSensor, and TouchSensor. Most of the time you want Pointer for mouse/touch and Keyboard for accessibility. Stack them with useSensors and pass the array to DndContext.
Collision detection is where you'll spend more time than you expect. closestCenter works for most lists. But if you're building a Kanban board where cards drop into columns, closestCorners gives you more intuitive snapping behavior — it measures the distance from the dragged item's corners to the target's corners instead of centers, which matters a lot when items have varying heights.
One more thing — the DragOverlay component. It's optional but you almost always want it. By default, the dragged item stays in the DOM and just visually moves. That can cause layout shifts and weird z-index issues. DragOverlay renders the dragged item into a portal at the root level, so it always sits above everything else with no stacking context problems. Pair it with an onDragStart handler that sets the active item in state, render that item inside DragOverlay, done.
Honestly, the biggest footgun in dnd-kit is forgetting that item IDs must be strings or numbers and must be unique across the entire DndContext — not just within a single list. If you've got two Kanban columns where both contain an item with id 1, you'll get silent misbehavior that's genuinely hard to trace.
Accessibility: the Part Everyone Skips
Drag and drop is notoriously hostile to keyboard users and screen reader users. That's not a small deal — in the US, accessibility compliance under WCAG 2.1 AA is a legal consideration for plenty of products, especially anything enterprise-facing.
dnd-kit ships KeyboardSensor with sensible defaults: arrow keys move items, Space picks up and drops, Escape cancels. It also generates appropriate aria-roledescription, aria-describedby, and live region announcements automatically. You do still need to wire up the screenReaderInstructions and announcements props on DndContext if you want custom messaging, but the defaults are genuinely usable.
react-beautiful-dnd was actually ahead of dnd-kit on this back in 2019. The keyboard support was excellent. That legacy advantage doesn't help you in 2026 when the whole library is unmaintained, but it's worth acknowledging the work Atlassian put in.
If your product has real accessibility requirements, test dnd-kit with VoiceOver or NVDA before shipping. The library does the heavy lifting, but your implementation details — things like whether the drag handle has a visible focus ring at outline-offset: 2px — still matter. Check out Empire UI's component library for accessible interactive patterns if you need reference implementations.
When You Might Skip Both and Use Something Else
Both libraries are great for list sorting and card-based layouts. Neither is the right tool for everything. If you're building a node graph editor, a drag-to-resize panel layout, or a 2D design canvas — think templates with complex interactive zones — you're probably better served by a specialized tool.
For node editors, @xyflow/react (formerly React Flow) owns that space. For resizable panels, react-resizable-panels from Bryan Vaughn is purpose-built and composable. For file upload drop zones specifically, react-dropzone is lighter and more focused than either dnd library.
That said, dnd-kit is surprisingly extensible. The modifier system lets you constrain drag axes, snap to grid, or restrict movement to a container. The collision detection API is pluggable — you can write a custom algorithm that hits a backend for real-time position validation if you really need that. It's not a toy library.
One more thing — bundle size. @dnd-kit/core is around 10 kb gzipped. react-beautiful-dnd is closer to 24 kb. If you're building a page where drag and drop is a secondary feature, that difference matters for initial load time.
The Verdict
Use dnd-kit. It's maintained, it works with React 19, the bundle is lean, and the API is flexible enough to handle most production requirements without fighting the abstractions. Start with @dnd-kit/core and @dnd-kit/sortable — you likely don't need the full suite upfront.
If you're migrating from react-beautiful-dnd, the concepts map fairly cleanly. Droppable + Draggable becomes SortableContext + useSortable. The onDragEnd result object is structured differently but carries the same information. A medium-complexity Kanban board migration usually takes a day or two.
For the visual layer, remember that drag-and-drop interactions are a great opportunity to lean into motion design. Smooth 200ms transitions on transform and opacity, a subtle scale on the active drag item, a highlight on the drop target. It's not decorative — it communicates state. If you want to see what polished interactive UI looks like, browse the Empire UI component library or check out the glassmorphism components for inspiration on layered UI patterns.
Whichever library you pick, test it on mobile. Touch interactions on iOS and Android have different timing characteristics than mouse events, and the gap between "works on desktop" and "works on a 375px iPhone" is wider than you'd expect for drag and drop specifically.
FAQ
Technically it runs, but it's unmaintained and breaks with React 19 due to the deprecated ReactDOM.findDOMNode() dependency. Don't start new projects with it.
Yes — you nest multiple SortableContext instances inside one DndContext and track which container the active item is over via onDragOver. The official docs have a working multi-container example.
The drag hooks require client components since they rely on DOM APIs and event listeners. Mark your drag-enabled components with 'use client' and you're fine.
Vanilla dnd-kit re-renders the full list on drag. For 500+ items you'll want to combine it with @dnd-kit/sortable's virtualized strategy alongside a windowing library like react-virtual to avoid layout thrash.