Skip to content

sortable

Reorder a list. direction: 'auto' reads the layout from where the items sit, so a row, a column and a wrapping grid all work without configuration.

import { sortable } from '@arshad-shah/detent';
sortable(list, {
animation: 180,
onSort({ item, from, to }) {},
});
A single list
  • first
  • second
  • third
  • fourth

Lists sharing a group name pass items to each other. Drag an item from either column into the other.

group: 'docs-demo'
  • alpha
  • beta
  • gamma
  • delta
  • epsilon

Give the items tabindex="0" and they can be reordered without a pointer. Tab to an item, then:

Key What it does
Space Lift the item, or drop it if already lifted.
Move the lifted item one place.
Escape Cancel and put it back.

Each step is announced to screen readers through a live region the library creates and removes with the last sortable list.

Tab into the list, then press space
  • one
  • two
  • three
Option Type Default What it does
group string Lists sharing a name pass items to each other.
items string Which children are sortable. Defaults to every element child.
direction 'auto' | 'x' | 'y' | 'grid' 'auto' How the list reads. auto works it out from where the items sit.
animation number 180 Reorder animation in milliseconds. 0 turns it off.
autoScroll boolean | { threshold, speed } true Scroll the list when the pointer nears its edges.
keyboard boolean true Allow reordering with the keyboard.
zIndex number 20 Stacking order for the item being moved.
disabled boolean false Keep the binding but stop responding.

Activation options — distance, delay, tolerance, handle, cancel and touchAction — behave as they do for draggable.

Add data-detent-ignore to a child to exclude it from sorting.

sortable(list, {
onStart(item, from) {}, // return false to refuse
onMove(item, to) {},
onSort({ item, from, to }) {}, // once, on drop, only if it actually moved
onEnd(item, cancelled) {},
});

from and to are { container, index }.

import { orderOf } from '@arshad-shah/detent';
orderOf(list); // HTMLElement[] in current DOM order
orderOf(list, '.card'); // only children matching a selector

Scrolling is handled. Cached measurements are corrected whenever anything scrolls mid-drag, including the page itself. Auto-scroll re-runs the drop decision on every step, so a finger held against the edge of a list keeps reordering without moving.

A scrolling list keeps its swipe gesture. When the container scrolls itself, touchAction defaults to 'auto' so a finger can still reach items further down. The press delay is what separates a scroll from a lift.

RTL is respected. Under dir="rtl" a horizontal list reorders in the direction you drag, not the direction the DOM runs.