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

PropTypeDefault
items*T[]
onReorder*(next: T[]) => void
getKey*(item: T, index: number) => string | number
children*(item: T, index: number) => ReactNode
orientation"vertical" | "horizontal" | "grid"
classNamestring

* required

Configure Theme
Theme
Accent Color
Custom
Gray Family
Appearance
Radius
Scaling
Panel Style
Heading Font
Body Font