How to build an accessible ReorderList in React
Every row carries a real button as its drag handle, so the entire interaction is reachable by keyboard: Space or Enter picks a row up, ArrowUp/ArrowDown move it, Space or Enter drops it, and Escape restores the order captured at pick-up. The handle exposes aria-pressed so assistive technology can tell held from idle, and each transition — picked up, moved, dropped, cancelled — is announced through a polite role="status" region using interpolated catalog strings that carry the item name and its position out of the total. The handle sets touch-action: none so a touch drag is not stolen by the scroller, and meets the coarse-pointer target floor. Item names for announcements come from `name` when the label is not plain text, so a rich label never produces an empty announcement.
When to use a ReorderList
- A short, user-owned ordering such as playlist tracks, form fields, or dashboard cards
- Any list whose order is meaningful data the user is expected to change
- Touch surfaces where dragging a handle is the natural gesture
When not to use it
- Long or virtualized lists, where dragging to a far position is impractical — offer a "move to position" control
- Ordering that is derived rather than authored (sort by name, date, rank) — use a sortable DataTable
- Read-only lists — use List or StructuredList
Keyboard interactions
Role list, verified at WCAG 2.2-AA.
SpaceEnterArrowUpArrowDownEscapeTab
Common mistakes
Avoid: Rendering a drag handle with no keyboard path
Prefer: Keep the built-in handle, which is a real button with Space/Arrow/Escape support
Pointer-only reordering is unusable by keyboard and screen-reader users; it is the standing accessibility gap in pointer-only implementations
Avoid: Deriving row keys from array index
Prefer: Give every item a stable `id`
Index keys make React reuse the wrong DOM nodes as rows move, so focus and animation land on the wrong row
Example
<ReorderList
value={items}
onValueChange={setItems}
/>