Drag & drop

A reusable drag-and-drop context: wrap a surface in a DragDropProvider, mark droppable regions with DropZone, and make items draggable with Draggable plus a DragHandle grip.
Items lift into a floating ghost and reorder within or across zones, position-aware. It runs on iOS, Android, and the web from one PanResponder + Animated code path, and the grip is keyboard- and screen-reader-operable: press Space to grab, the arrow keys to move, Space to drop, Escape to cancel.
The reading direction is respected throughout. In a right-to-left locale a horizontal zone lays its items out right to left, so the first position is the rightmost one, the drop indicator meets the row's right edge, and the two horizontal arrows swap roles (Left Arrow moves forward, Right Arrow back, per the WAI-ARIA practices). Vertical lists and the up/down arrows read the same way in both directions.
Design review
Write the tests
Ship the release
iOS
Design review
Write the tests
Ship the release
Android
Design review
Write the tests
Ship the release
Web
tsx
<Stateful initial={[
{ id: "a", zone: "list", title: "Design review" },
{ id: "b", zone: "list", title: "Write the tests" },
{ id: "c", zone: "list", title: "Ship the release" },
]}>
{(cards, setCards) => (
<DragDropProvider>
<DropZone id="list" label="Tasks" onDrop={(e) => setCards(applyDrop(cards, e))}>
<Column snug>
{cards.map((c) => (
<Draggable key={c.id} id={c.id} data={c} label={c.title}>
<Card>
<Row between alignCenter>
<Typography>{c.title}</Typography>
<DragHandle label={`Reorder ${c.title}`} />
</Row>
</Card>
</Draggable>
))}
</Column>
</DropZone>
</DragDropProvider>
)}
</Stateful>

Props

DragDropProvider

Prop
Description
children
required
ReactNode
—
style
StyleProp<ViewStyle>
Outer layout composition only (width/flex within a parent), never a restyle hook. The provider wraps the whole draggable surface and hosts the floating drag ghost.
testID
string
E2E hook forwarded to the root element.

DropZone

Prop
Description
id
required
string
Stable id for this drop target, used in the drop event and for keyboard navigation.
label
required
string
Accessible name announced when a drag hovers or drops here (e.g. the column title).
onDrop
(e: DropEvent) => void
Fired on THIS zone when an item is dropped into it (from any zone, including itself).
horizontal
boolean
Lay the items out in a row so the insertion axis is horizontal (default is a vertical column).
disabled
boolean
Refuse drops: the zone is skipped by hit-testing and the keyboard cursor, and never highlights.
children
ReactNode
—
style
StyleProp<ViewStyle>
Outer layout composition only (width/flex within a parent), never a restyle hook.
testID
string
E2E hook forwarded to the root element.

Draggable

Prop
Description
id
required
string
Stable id for this item (unique within the provider).
data
required
T
Arbitrary payload handed back in the drop event.
label
string
Accessible name of the item, used in drag announcements (falls back to `id`).
disabled
boolean
Disable dragging: the grip inside becomes inert.
preview
ReactNode
Custom floating drag preview; defaults to the item's own `children`.
children
ReactNode
—
style
StyleProp<ViewStyle>
Outer layout composition only (width/flex within a parent), never a restyle hook.
testID
string
E2E hook forwarded to the root element.

DragHandle

Prop
Description
label
string
Accessible name for the grip (e.g. "Reorder Rotate webhook secrets").
testID
string
E2E hook forwarded to the root element.
style
StyleProp<ViewStyle>
Outer layout composition only (width/flex within a parent), never a restyle hook.

Do & Don’t

Grip and fallback

Don’t
Design review (drag the whole card?)
Don't make a bare card the only drag surface with no grip and no alternative: whole-card dragging fights taps and scrolling, and it leaves keyboard and screen-reader users with no way to move the item at all.
Do
Design review
Give each item a labeled DragHandle and keep a non-drag way to move it. The grip is a keyboard tab stop that grabs on Space and moves with the arrows; a RowMenu "Move to" beside it is a good extra path for pointer and assistive-tech users.