Sortable
Zero-dependency HTML5 drag-and-drop list re-ordering primitive with render-prop item formatting and grip handle.
// Plain markup
import { Sortable, SortableHandle } from "@shavin/ui";Vertical list (with handle)
- Subscriptions
- Loans & EMIs
- Utilities
- Insurance
const [list, setList] = useState([...]);
<Sortable items={list} onReorder={setList} getKey={(x) => x}>
{(item) => (
<div className="flex items-center gap-3 rounded-[var(--radius-lg)] border border-hairline bg-surface px-3 py-2.5">
<SortableHandle />
<span className="text-sm">{item}</span>
</div>
)}
</Sortable>Horizontal (chips)
- Design
- Engineering
- Product
- Ops
- Sales
<Sortable items={tags} onReorder={setTags} getKey={(x) => x} orientation="horizontal">
{(tag) => (
<span className="rounded-full border border-hairline bg-surface px-3 py-1 text-sm">{tag}</span>
)}
</Sortable>Grid (cards)
- Alpha
- Beta
- Gamma
- Delta
- Epsilon
- Zeta
<Sortable items={cards} onReorder={setCards} getKey={(x) => x} orientation="grid">
{(card) => (
<div className="flex h-20 items-center justify-center rounded-[var(--radius-panel)] border border-hairline bg-surface text-sm font-medium">
{card}
</div>
)}
</Sortable>Props
| Prop | Type | Default |
|---|---|---|
| items* | T[] | — |
| onReorder* | (next: T[]) => void | — |
| getKey* | (item: T, index: number) => string | number | — |
| children* | (item: T, index: number) => ReactNode | — |
| orientation | "vertical" | "horizontal" | "grid" | — |
| className | string | — |
* required