Sortable List (drag and drop)
Preview: Cards
Reorder by drag, or keyboard (focus the handle, Enter, arrows, Enter). Built on React Aria drag and drop.
Import
import { SortableList } from '@marbleui/ui'Props
<SortableList>
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label *required | string | — | |
| children *required | (item: T) => ReactNode | — | |
| items *required | T[] | — | |
| onReorder *required | (items: T[]) => void | — | |
| className | string | — | |
| getText | (item: T) => string | (i) => String(i.id) | |
| variant | 'divided' | 'cards' | 'cards' |
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Button · React Aria DropIndicator · React Aria GridList · React Aria GridListItem. Follow the primitive's docs for the full keyboard map.
Usage notes
List that users can reorder by dragging or with the keyboard. For a static list use ListView; for moving cards between columns use Kanban.
<SortableList aria-label="Roadmap" items={items} onReorder={setItems} getText={(i) => i.title}>
{(i) => <span>{i.title}</span>}
</SortableList>
- Controlled: you own
itemsand receive the new order inonReorder. Every item needs a uniqueid. aria-labelis required. Keyboard: focus the drag handle, press Enter, move with the arrow keys, press Enter to drop.getTextsupplies the plain-text label for each item (defaults to itsid); set it so drags and screen readers get a readable name.variantiscards(default) ordivided.
Example source · show· hide
import { useState } from 'react'
import { Badge } from '../badge'
import { SortableList } from './sortable-list'
const initial = [
{ id: 'research', title: 'User research', tone: 'success' as const, tag: 'Done' },
{ id: 'wireframes', title: 'Wireframes', tone: 'accent' as const, tag: 'In progress' },
{ id: 'visual', title: 'Visual design', tone: 'neutral' as const, tag: 'Todo' },
{ id: 'build', title: 'Build components', tone: 'neutral' as const, tag: 'Todo' },
{ id: 'launch', title: 'Launch', tone: 'neutral' as const, tag: 'Todo' },
]
function Demo({ variant }: { variant: 'cards' | 'divided' }) {
const [items, setItems] = useState(initial)
return (
<SortableList aria-label="Roadmap" items={items} onReorder={setItems} getText={(i) => i.title} variant={variant} className="w-80">
{(i) => (
<div className="flex items-center justify-between gap-2">
<span>{i.title}</span>
<Badge tone={i.tone}>{i.tag}</Badge>
</div>
)}
</SortableList>
)
}
export const styles = { Cards: () => <Demo variant="cards" />, Divided: () => <Demo variant="divided" /> }Source: packages/ui/src/sortable-list/sortable-list.tsx
Catalog
- Category
- collections
- Priority
- Core
- Primitive
- react-aria
- React Aria
- useDraggableCollection + useDroppableCollection