react-beautiful-dnd, react-easy-sort, react-sortable-hoc, and react-sortable-tree are React libraries that enable drag-and-drop and reordering functionality, but they target different interaction models. react-beautiful-dnd provides polished, accessible drag-and-drop for flat lists and multi-container scenarios like Kanban boards. react-easy-sort offers a minimal, hooks-based solution for simple single-container reordering. react-sortable-hoc (now deprecated) used higher-order components to add sorting behavior to any list but relies on outdated React patterns. react-sortable-tree specializes in interactive, nested tree structures such as file explorers or organizational charts, supporting drag-and-drop between hierarchical nodes.
When building interactive UIs in React — like task boards, reorderable lists, or nested tree editors — you’ll likely need a library to handle drag-and-drop (DnD) and sorting. The ecosystem offers several options, each with different strengths, constraints, and design philosophies. Let’s compare four popular packages: react-beautiful-dnd, react-easy-sort, react-sortable-hoc, and react-sortable-tree.
react-beautiful-dnd is built for vertical or horizontal lists with rich DnD interactions. It supports keyboard navigation, auto-scrolling, and smooth animations. Best suited for Kanban boards, playlists, or any flat list where items can be reordered or moved between containers.
react-easy-sort focuses on minimalist, lightweight reordering of items within a single container using mouse or touch. It doesn’t support cross-container dragging but excels at simple “reorder this list” use cases without heavy dependencies.
react-sortable-hoc is a higher-order component (HOC) that wraps any component to make it sortable. It supports both vertical and horizontal lists, multiple containers, and custom rendering. However, it relies on legacy React patterns (class components, HOCs) and hasn’t kept pace with modern hooks-based architecture.
react-sortable-tree is specialized for interactive, nested tree structures — like file explorers, org charts, or category hierarchies. It supports drag-and-drop between nodes, expand/collapse, and deep nesting, but isn’t meant for flat lists.
⚠️ Deprecation Note: As of 2023,
react-sortable-hocis officially deprecated. Its GitHub repo states: “This project is no longer maintained. We recommend using @dnd-kit/sortable instead.” New projects should avoid it.
react-beautiful-dnd requires wrapping your app in a DragDropContext, then using Droppable and Draggable components:
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
function TaskList({ tasks, onDragEnd }) {
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="tasks">
{(provided) => (
<ul {...provided.droppableProps} ref={provided.innerRef}>
{tasks.map((task, index) => (
<Draggable key={task.id} draggableId={task.id} index={index}>
{(provided) => (
<li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{task.content}
</li>
)}
</Draggable>
))}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
);
}
react-easy-sort uses a simpler hook-like pattern with a useSortable function and a wrapper component:
import { useSortable, SortableContainer, SortableItem } from 'react-easy-sort';
function SimpleList({ items, onChange }) {
const { items: sortedItems, onSortEnd } = useSortable(items, onChange);
return (
<SortableContainer onSortEnd={onSortEnd}>
{sortedItems.map((item) => (
<SortableItem key={item.id}>
<div>{item.name}</div>
</SortableItem>
))}
</SortableContainer>
);
}
react-sortable-hoc (deprecated) wraps components via HOCs:
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
const SortableItem = SortableElement(({ value }) => <li>{value}</li>);
const SortableList = SortableContainer(({ items }) => (
<ul>
{items.map((item, index) => (
<SortableItem key={`item-${index}`} index={index} value={item} />
))}
</ul>
));
// Usage
<SortableList items={items} onSortEnd={onSortEnd} />
react-sortable-tree is not designed for flat lists — attempting to use it here would be overkill and awkward.
Only react-sortable-tree handles this natively:
import SortableTree from 'react-sortable-tree';
function OrgChart({ treeData, onChange }) {
return (
<SortableTree
treeData={treeData}
onChange={onChange}
canDrag={({ node }) => !node.isLocked}
canDrop={({ nextParent }) => nextParent && !nextParent.isReadOnly}
/>
);
}
The other libraries cannot represent hierarchical relationships or allow dragging between nested levels.
Moving items between separate lists (e.g., “To Do” → “Done”) is common in task apps.
react-beautiful-dnd supports this out of the box by giving each Droppable a unique droppableId and handling the move logic in onDragEnd:
const onDragEnd = (result) => {
const { source, destination } = result;
if (!destination) return;
if (source.droppableId === destination.droppableId) {
// Reorder within same list
} else {
// Move between lists
}
};
react-easy-sort does not support cross-container dragging — all items must live in one SortableContainer.
react-sortable-hoc supports it via the group prop or by managing multiple sortable lists manually, but again, it’s deprecated.
react-sortable-tree allows moving nodes between branches of the same tree, but not between entirely separate trees or flat lists.
react-beautiful-dnd includes keyboard navigation, screen reader support, and smooth drop animations. It also auto-scrolls when dragging near container edges.
react-easy-sort provides basic mouse/touch support but lacks built-in keyboard accessibility or advanced UX features.
react-sortable-hoc leaves accessibility entirely up to the developer.
react-sortable-tree includes expand/collapse toggles and visual indentation but minimal ARIA support.
react-beautiful-dnd: Works with modern React but uses render props instead of hooks. Still actively maintained.react-easy-sort: Built with hooks and functional components. Lightweight and modern.react-sortable-hoc: Relies on class components and HOCs — incompatible with React Server Components and discouraged in new codebases.react-sortable-tree: Uses class components internally; works in most client-side apps but not optimized for concurrent mode.react-beautiful-dndreact-easy-sortreact-sortable-treereact-sortable-hoc unless absolutely necessary. Migrate to @dnd-kit or react-beautiful-dnd.| Feature | react-beautiful-dnd | react-easy-sort | react-sortable-hoc | react-sortable-tree |
|---|---|---|---|---|
| Flat List Sorting | ✅ Excellent | ✅ Good (single container) | ✅ Good (deprecated) | ❌ Not intended |
| Cross-Container Drag | ✅ Yes | ❌ No | ✅ Yes (deprecated) | ⚠️ Within same tree only |
| Nested Trees | ❌ No | ❌ No | ❌ No | ✅ Excellent |
| Keyboard Accessibility | ✅ Full support | ❌ None | ❌ None | ⚠️ Partial |
| Modern React (Hooks) | ⚠️ Render props | ✅ Hooks | ❌ Class/HOC | ⚠️ Class-based |
| Maintenance Status | ✅ Actively maintained | ✅ Actively maintained | ❌ Deprecated | ✅ Maintained (niche use) |
react-beautiful-dnd.react-easy-sort.react-sortable-tree.react-sortable-hoc — it’s deprecated and out of step with modern React practices.Each library solves a specific problem well. Pick the one that matches your UI’s structure — not the one with the most stars.
Choose react-easy-sort for simple, single-container reordering with minimal overhead. It’s perfect for settings pages, small admin panels, or any scenario where you just need to reorder items in one list without cross-container moves. Don’t use it if you require keyboard accessibility, multi-list interactions, or complex drag behaviors.
Choose react-beautiful-dnd when you need a robust, production-ready drag-and-drop experience for flat lists or multi-container layouts like task boards. It includes keyboard navigation, smooth animations, and auto-scrolling, making it ideal for applications where UX polish and accessibility are critical. Avoid it if you’re working with deeply nested tree structures or need an ultra-lightweight solution.
Do not choose react-sortable-hoc for new projects — it is officially deprecated and unmaintained. While it once provided flexible sorting via higher-order components, its reliance on class-based patterns makes it incompatible with modern React best practices. If you encounter it in legacy code, plan a migration to react-beautiful-dnd or @dnd-kit.
Choose react-sortable-tree exclusively when your UI requires interactive, nested tree structures — such as file browsers, category editors, or org charts. It supports drag-and-drop between nodes, expand/collapse toggles, and deep hierarchy visualization. Avoid it for flat lists or general-purpose DnD, as it’s over-engineered for those use cases.
A React component to sort items in lists or grids

The goal of this component is to allow sorting elements with drag and drop.
It is mobile friendly by default. It doesn't block scrolling the page when swiping inside it: the user needs to press an item during at least 200ms to start the drag gesture.
On non-touch devices, the drag gesture only starts after moving an element by at least one pixel. This is done to avoid blocking clicks on clickable elements inside an item.
Check out the examples:
yarn add react-easy-sort
or
npm install react-easy-sort --save
import SortableList, { SortableItem } from 'react-easy-sort'
import { arrayMoveImmutable } from 'array-move'
const App = () => {
const [items, setItems] = React.useState(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'])
const onSortEnd = (oldIndex: number, newIndex: number) => {
setItems((array) => arrayMoveImmutable(array, oldIndex, newIndex))
}
return (
<SortableList onSortEnd={onSortEnd} className="list" draggedItemClassName="dragged">
{items.map((item) => (
<SortableItem key={item}>
<div className="item">{item}</div>
</SortableItem>
))}
</SortableList>
)
}
| Name | Description | Type | Default |
|---|---|---|---|
| as | Determines html tag for the container element | keyof JSX.IntrinsicElements | div |
| onSortStart | Called when the user starts a sorting gesture | () => void | - |
| onSortMove | Called when the dragged item changes position during a sorting gesture | (newIndex: number) => void | - |
| onSortEnd* | Called when the user finishes a sorting gesture. | (oldIndex: number, newIndex: number) => void | - |
| draggedItemClassName | Class applied to the item being dragged | string | - |
| lockAxis | Determines if an axis should be locked | 'x' or 'y' | |
| allowDrag | Determines whether items can be dragged | boolean | true |
| customHolderRef | Ref of an element to use as a container for the dragged item | React.RefObject<HTMLElement | null> | document.body |
| dropTarget | React element to use as a dropTarget | ReactNode | |
| autoScroll | Determines whether the containing element (or window) should scroll during sort | boolean | false |
This component doesn't take any other props than its child. This child should be a single React element that can receives a ref. If you pass a component as a child, it needs to be wrapped with React.forwardRef().
You can use this component if you don't want the whole item to be draggable but only a specific area of it.
import SortableList, { SortableItem, SortableKnob } from 'react-easy-sort'
import arrayMove from 'array-move'
const App = () => {
const [items, setItems] = React.useState(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'])
const onSortEnd = (oldIndex: number, newIndex: number) => {
setItems((array) => arrayMove(array, oldIndex, newIndex))
}
return (
<SortableList onSortEnd={onSortEnd} className="list" draggedItemClassName="dragged">
{items.map((item) => (
<SortableItem key={item}>
<div className="item">
<SortableKnob>
<div>Drag me</div>
</SortableKnob>
{item}
</div>
</SortableItem>
))}
</SortableList>
)
}
This component doesn't take any other props than its child. This child should be a single React element that can receive a ref. If you pass a component as a child, it needs to be wrapped with React.forwardRef().
To disable browser default behaviors that can interfer with the dragging experience, we recommend adding the following declarations on the "items":
user-select: none;: disable the selection of content inside the item (the blue box)pointer-events: none;: required for some browsers if your items contain images (see the Interactive avatars demo)yarn
yarn start
Now, open http://localhost:3001/index.html and start hacking!
This project is maintained by Valentin Hervieu.
This project was originally part of @ricardo-ch organisation because I (Valentin) was working at Ricardo. After leaving this company, they gracefully accepted to transfer the project to me. ❤️