Sortable Stack

List the user can reorder by dragging or keyboard.

Drag a handle to reorder. With a handle focused, press Up or Down to move the row, Home or End to move it to the top or bottom.

  • 1Ship pricing pageDue Fri
  • 2Triage checkout bugs4 open
  • 3Review design candidates3 portfolios
  • 4Update API docsv2.3
  • 5Write sprint retro15 min

Installation

pnpm dlx shadcn@latest add @paneui/sortable-stack

Usage

Composition

Examples

Controlled

Use when you save the order or show it elsewhere.

Move to Top

Custom Undo

Use when undo belongs in your own control instead of the built-in pill.

Disabled

Keyboard

Rows move only from their handle. With a handle focused, Up/Down move the row one step and Home/End send it to the top or bottom; focus stays on the handle and the new position is announced.

Motion

The dragged row scales to 1.025. With reduced motion rows jump into place without scaling.

API Reference

SortableStack

Drag-to-reorder list. Items need a string id.

items
T[]
Controlled rows in order.
defaultItems= []
T[]
Initial rows when uncontrolled.
onItemsChange
(items: T[]) => void
Called with the new order after a drag, key move or undo.
getItemLabel*
(item: T) => string
Readable name per row, used in handle labels and announcements.
children*
(items: T[]) => ReactNode
Render the rows in their current order.
label= "Sortable list"
string
Accessible name of the list.
disabled= false
boolean
Lock the order; handles are disabled and rows can't be dragged.
showUndo= true
boolean
Render the Undo pill under the list.
footer
ReactNode
Extra content under the list, inside the stack's context.
className
string
Merged onto the wrapper.
PropTypeDefaultDescription
itemsT[]—Controlled rows in order.
defaultItemsT[][]Initial rows when uncontrolled.
onItemsChange(items: T[]) => void—Called with the new order after a drag, key move or undo.
getItemLabel*(item: T) => string—Readable name per row, used in handle labels and announcements.
children*(items: T[]) => ReactNode—Render the rows in their current order.
labelstring"Sortable list"Accessible name of the list.
disabledbooleanfalseLock the order; handles are disabled and rows can't be dragged.
showUndobooleantrueRender the Undo pill under the list.
footerReactNode—Extra content under the list, inside the stack's context.
classNamestring—Merged onto the wrapper.

SortableStackItem

One row.

id*
string
The item's id.
className
string
Merged onto the row.
style
CSSProperties
Inline style for the row.
children
ReactNode
Handle and content.
PropTypeDefaultDescription
id*string—The item's id.
classNamestring—Merged onto the row.
styleCSSProperties—Inline style for the row.
childrenReactNode—Handle and content.
data-dragging
Present while the row is dragged.
Data attributeDescription
data-draggingPresent while the row is dragged.

SortableStackHandle

Grip button that starts the drag and owns keyboard reordering. Labelled with the row's name and position. Also accepts every prop of button.

children= grip icon
ReactNode
Replace the grip icon.
PropTypeDefaultDescription
childrenReactNodegrip iconReplace the grip icon.

SortableStackContent

Content area of a row. Also accepts every prop of div.

SortableStackUndo

Restores the order from before the last move. Hidden until there is something to undo. Also accepts every prop of button.

data-visible
Present while an undo is available.
Data attributeDescription
data-visiblePresent while an undo is available.

useSortableStack

Order and actions for custom controls. Use inside SortableStack.

ids
string[]
Current order of ids.
moveItem
(id: string, by: -1 | 1 | "first" | "last") => void
Move a row; undoable.
undo
() => void
Restore the previous order.
canUndo
boolean
Whether an undo is available.
disabled
boolean
Whether the stack is disabled.
PropTypeDefaultDescription
idsstring[]—Current order of ids.
moveItem(id: string, by: -1 | 1 | "first" | "last") => void—Move a row; undoable.
undo() => void—Restore the previous order.
canUndoboolean—Whether an undo is available.
disabledboolean—Whether the stack is disabled.