Ballmac UI home

Sortable

Reorder lists, grids and multi-column boards by pointer, touch or keyboard, with spoken announcements, move buttons for people who cannot drag, a floating lifted item and reduced motion.

Morning StaticLena Okoro3:42
Paper BoatsThe Quiet Hours4:08
Harbor LightsDev Anand2:56
Slow OrbitMira & Co.5:15
Nine Floors UpJun Matsuda3:21

Installation

$ pnpm dlx shadcn@latest add @ballmac/sortable

Usage

import { Sortable, SortableGroup, SortableGroups, SortableHandle, SortableItem, SortableMove } from "@/components/ballmac/sortable"

The full example is in the Code tab above.

Examples

Dashboard grid

Revenue$48.2k+12% this week
Sign-ups1,284+4% this week
Churn1.9%−0.3 pts
p95 latency182 msWithin target
Open tickets379 waiting on us
NPS58Last 30 days

Horizontal chips

Order the filters. The first one is the default.

Design
Engineering
Marketing
Support
Finance
People

Board with columns

To do3

Fix the sign-in redirect
Invoice PDF layout
Document mirrored layouts

In progress2

Draw empty states
Rate limit the public API

Done2

Write release notes
CSV export for reports

With move buttons

Collect payment details
Confirm the shipping address
Choose a delivery slot
Review the order
Place the order

Drag the grip, use Space and the arrow keys on it, or press the arrows.

API reference

<Sortable>

PropTypeDefault
value*

The ids of the items, in order.

string[]—
onValueChange*

Called with the new order when an item is dropped or moved with a button. Keep it in state and render the items in that order.

(ids: string[], info: SortableInfo) => void—
label*

What the list is, read by screen readers: "Playlist".

string—
orientation

"vertical" (default) and "horizontal" lists move on one axis; "grid" moves anywhere.

SortableOrientation"vertical"
getLabel

Says each item's name when it is picked up and dropped. Defaults to the item's `label`, then its id.

(id: string) => string—
disabledbooleanfalse
overlay

Draw the lifted item in a floating layer under the pointer. Turn off for items that need to stay in place.

booleantrue
children*

The items: `SortableItem`s, in the same order as `value`.

React.ReactNode—

<SortableItem>

PropTypeDefault
id*

The item's id, one of the ids in `value`.

string—
label

The item's name for screen readers: "Chorus".

string—
dragEverywhere

Drag the whole item. Leave it off and add a `SortableHandle` for items that hold buttons, links or inputs.

booleanfalse
disabledboolean—

<SortableHandle>

PropTypeDefault
children

Replace the grip icon.

React.ReactNode—

<SortableMove>

PropTypeDefault
direction*

Which way the item goes: toward the start of the list, or toward the end.

"previous" | "next"—
childrenReact.ReactNode—

<SortableGroups>

PropTypeDefault
value*

The ids in each group: `{ todo: ["a", "b"], doing: ["c"] }`. The object's key order is the order of the groups.

SortableGroupsValue—
onValueChange*

Called with the new arrangement when an item is dropped or moved with a button. Keep it in state and render the items from it.

(groups: SortableGroupsValue, info: SortableGroupsInfo) => void—
label*

What the set of lists is: "Board".

string—
getLabel(id: string) => string—
getGroupLabel

Says a group's name in announcements. Defaults to the group id.

(groupId: string) => string—
orientation

Orientation of the items inside each group.

SortableOrientation"vertical"
disabledbooleanfalse
children*React.ReactNode—

<SortableGroup>

PropTypeDefault
id*

The group's key in `value`.

string—
label

The group's name for screen readers. Defaults to `getGroupLabel(id)`.

string—

Also accepts the standard attributes of its root element.

Accessibility

KeyAction
Space / Enter on the handlePicks up the item; the same keys drop it
Arrow keysMove the lifted item; the new position is announced
EscapeCancels and puts the item back
SortableMove buttonsMove an item one place with a click, for people who cannot drag (WCAG 2.5.7)
Screen readersInstructions, picked-up, over, dropped and cancelled messages are read out, in the page's language

Quality scorecard

7/7

Every applicable check passes

Measured in Chromium 153.0.8010.12 on October 19, 2026, on version 1.0.0, across 5 examples.

How we measure
  • Passed: Accessibility scan

    No serious or critical issues in light and dark, on a phone and a desktop (axe-core 4.13.0, WCAG 2.1 AA and best-practice rules).

  • Passed: Small screens

    No sideways scrolling at 360, 390 or 1280 px wide.

  • Passed: Reduced motion

    No endless animation to begin with.

  • Passed: Right to left

    Mirrors correctly in Arabic, Hebrew, Persian and Urdu layouts (0% of elements off the mirror, limit 15%).

  • Passed: Clean console

    No console errors or React hydration errors while loading.

  • Passed: Keyboard

    37 tab stops, each showing a visible change when it takes focus. 5 documented keys.

  • Not applicable: Loading, empty and error

    Nothing loads or fails: it reorders the items it is given; an empty list or group simply draws no items.

  • Passed: Automated tests

    Covered by sortable.test.tsx.

  • For information: Footprint

    7.4 KB of source (gzip) in 1 file. Installs @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities, motion and 1 more.

Automated checks find a subset of problems and do not replace testing with a screen reader and a keyboard in your own app. What they miss.

Use with AI

Hold the order in state: <Sortable label value={ids} onValueChange={setIds}> with a <SortableItem id> per id, each holding a <SortableHandle>. SortableGroups and SortableGroup move items between lists. With the shadcn MCP server set up (guide), ask your agent:

Add the Ballmac UI Sortable (@ballmac/sortable) to this project with the shadcn MCP, then use it where it fits.

Use it for

  • Playlists, checklists, filters and settings people put in their own order
  • Dashboard tiles in a grid and chips in a row
  • Boards where cards move between columns

Not for

  • A full kanban product with cards, tags and assignees; use kanban-board
  • Sorting by a column's value; use data-table or data-grid
  • Reordering rows of a huge virtualized list

Registry JSON: https://ui.ballmac.com/r/sortable.json

Credits

Based on dnd-kit, adapted by Ballmac. Free to use in personal and commercial projects.

Ballmac UI Pro

Need the whole screen, not one piece?

150 premium blocks (dashboards, settings, calendars, mail, shops, onboarding), a private shadcn registry and Figma tokens. Blocks that show data come with loading, empty and error states built in.

Not ready? Try three Pro blocks free

Leave your email and the blocks appear right here, ready to copy. Yours to use in any project.