Skip to content

React

Terminal window
npm install @arshad-shah/detent @arshad-shah/detent-react

Each hook returns a callback ref. Attach it to the element you want bound.

import { useSortable } from '@arshad-shah/detent-react';
import '@arshad-shah/detent/styles.css';
function List({ items, onReorder }) {
const ref = useSortable({
animation: 180,
onSort: ({ from, to }) => onReorder(from.index, to.index),
});
return (
<ul ref={ref}>
{items.map((item) => (
<li key={item.id}>{item.label}</li>
))}
</ul>
);
}
Hook Binds
useDraggable(options?) draggable
useSortable(options?) sortable
useResizable(options?) resizable

Options are the core library’s — follow the links above for every one.

Pass inline objects and arrow functions freely:

const ref = useDraggable({
bounds: 'parent',
onEnd: (event) => save(event.offset), // recreated every render, and fine
});

Options are read through a ref at call time, so a re-render mid-drag does not interrupt it, and callbacks are always the newest ones. You do not need useCallback or useMemo here.

This is the whole reason the package exists. A binding written the obvious way — a useEffect with options in its dependency array — tears the drag down and rebuilds it on every render, cancelling the gesture in progress.

The hooks call destroy() when the element unmounts or the ref changes. You do not need an effect for it.

Peer react >= 18; works with 19. No react-dom dependency.