A drag-and-drop system for React Native
Visit the official documentation
Drax is a declarative drag-and-drop framework for React Native, written in TypeScript. It supports free-form drag-and-drop, sortable lists and grids, cross-list reorder, drag handles, collision algorithms, and more.
Built on Reanimated 4 and Gesture Handler 3 with a UI-thread-first architecture for smooth 60fps interactions.
Platforms: iOS, Android, Web
acceptsDrag| Feature | Drax | reanimated-dnd | sortables |
|---|---|---|---|
| Free-form drag & drop | ✅ | ✅ | ➖ |
| Sortable list / grid | ✅ | ✅ | ✅ |
| Mixed-size grid (non-uniform spans) | ✅ | ➖ | ➖ |
| Cross-container / cross-list reorder | ⚠️ Experimental | ➖ | ➖ |
| List-agnostic (FlatList, FlashList, LegendList) | ✅ | ➖ | ➖ |
| Drag handles | ✅ | ✅ | ✅ |
| Drag state styling | 15 props + inactive | onStateChange | 5 props + hook |
| Drop indicator | ✅ | ➖ | Grid only |
| UI-thread DnD collision | ✅ | ➖ | ➖ |
| Event callbacks | 19 types | ~12 types | ~10 types |
| Accessibility + reduced motion | ✅ | Manual | Manual |
| Web support | ✅ | ➖ | Partial |
| Reanimated | ≥ 4 | ≥ 4.2 | ≥ 3 |
| Gesture Handler | ≥ 2 | ≥ 2.28 | ≥ 2 |
npm install react-native-drax
# or
yarn add react-native-draxnpm install react-native-reanimated react-native-gesture-handler| Peer Dependency | Version |
|---|---|
react |
>= 18 |
react-native |
>= 0.68 |
react-native-reanimated |
^4.0.0 |
react-native-gesture-handler |
>= 2.0.0 (v3 recommended) |
import { DraxProvider, DraxView } from 'react-native-drax';
function App() {
return (
console.log('dragging')}
payload="hello"
/>
{
console.log(`received: ${payload}`);
}}
/>
);
}The simplest way to make a reorderable list:
import { useState } from 'react';
import { Text, View } from 'react-native';
import { DraxProvider, DraxList } from 'react-native-drax';
function App() {
const [items, setItems] = useState(['A', 'B', 'C', 'D', 'E']);
return (
item}
onReorder={({ data }) => setItems(data)}
renderItem={({ item }) => (
{item}
)}
/>
);
}DraxList is list-agnostic — pass any list component via the component prop:
import { FlashList } from '@shopify/flash-list';
item.id}
onReorder={({ data }) => setItems(data)}
renderItem={({ item }) => }
estimatedItemSize={60}
/>For full control, use the composable primitives directly:
import {
DraxProvider,
useSortableList,
SortableContainer,
SortableItem,
} from 'react-native-drax';
import { FlatList } from 'react-native';
function App() {
const [items, setItems] = useState(['A', 'B', 'C', 'D', 'E']);
const listRef = useRef(null);
const sortable = useSortableList({
data: items,
keyExtractor: (item) => item,
onReorder: ({ data }) => setItems(data),
});
return (
(
{item}
)}
/>
);
}This pattern works with any list component — FlatList, FlashList, LegendList, ScrollView, etc.
Only the handle area starts a drag — the rest of the view scrolls normally:
import { DraxView, DraxHandle } from 'react-native-drax';
This content scrolls normally
{/* Only this starts a drag */}
Constrain a dragged view within a boundary:
const boundsRef = useRef(null);
I can only be dragged within the parent
Control how receiver detection works:
{/* hover center inside receiver (default) */}
{/* any overlap triggers receiving */}
{/* dragged view must be fully inside */}Accept or reject drops conditionally:
items.length addItem(dragged.payload)}
/>Style the hover layer based on drag state:
Snap dropped items to specific positions within a receiver:
import { snapToAlignment } from 'react-native-drax';
snapToAlignment(receiver, dragged, 'top-left', { x: 8, y: 8 })
}
/>Alignments: center, top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right
{/* spring physics */}
{/* soft spring */}
{/* fast spring */}
{/* instant */}
{/* Or custom: */}
Device reduced motion settings are respected automatically.
{ /* fires every frame while dragging over empty space */ }}
onDragOver={(data) => { /* fires every frame while over the same receiver */ }}
/>Move items between lists (cross-list reorder):
import {
useSortableBoard,
SortableBoardContainer,
useSortableList,
SortableContainer,
SortableItem,
} from 'react-native-drax';
const board = useSortableBoard({
onTransfer: ({ fromColumnId, toColumnId, fromIndex, toIndex, item }) => {
// Move item between columns
},
});
{columns.map((column) => (
))}
Note: Cross-container drag is experimental. The API may change in future versions.
For convenience, all components are available under the Drax namespace:
import { Drax } from 'react-native-drax';
| Component | Description |
|---|---|
DraxProvider |
Context provider — wrap your app's drag-and-drop area |
DraxView |
Draggable and/or receptive view with 19 callback events |
DraxList |
List-agnostic sortable list (convenience wrapper) |
DraxHandle |
Drag handle — only this area starts a drag |
DraxScrollView |
Scrollable container with auto-scroll during drag |
SortableContainer |
Monitoring wrapper for composable sortable API |
SortableItem |
Per-item wrapper with shift animation |
SortableBoardContainer |
Cross-container board coordinator (experimental) |
| Hook | Description |
|---|---|
useSortableList |
List-agnostic reorder state management |
useSortableBoard |
Cross-container board coordinator (experimental) |
useDraxContext |
Access the Drax context |
useDraxId |
Generate a unique Drax view ID |
The example/ directory contains an Expo Router app with 11 screens demonstrating all features:
| Screen | Features shown |
|---|---|
| Color Drag & Drop | Drop acceptance, hover styles, snap alignment |
| Reorderable List | DraxList, animation presets, auto-scroll |
| Reorderable Grid | Sortable grid with multi-column layout |
| Mixed-Size Grid | Items with different spans (1×1, 2×2, etc.) using getItemSpan and packGrid |
| Drag Handles | Only the grip icon starts a drag |
| Drag Bounds | Constrain drag within a view |
| Collision Modes | Center vs Intersect vs Contain |
| Cross-List Reorder | FlashList + LegendList + FlatList cross-container drag (experimental) |
| Knight Moves | Chess knight drag puzzle |
| Scrolling | Drag from scroll view to drop zone |
| Stress Test | 100 items in a sortable list |
To run the example app:
cd example && yarn startv1.0.0 is a complete rewrite. Key changes:
DraxList is new — list-agnostic convenience wrapper using the new sortable architecture. The old DraxList / DraxListItem API from v0.10.x and v0.11.0-alpha is removed.useSortableList + SortableContainer + SortableItem composable API replaces the old array-indexed approach.See CHANGELOG.md for a full list of changes.
Issues, pull requests, and discussion are all welcome. See the Contribution Guidelines for details.
This project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
Originally created by Joe Lafiosca. v1.0.0 rewrite led by Ovidiu Cristescu.
Thanks to all contributors who have helped make Drax better:
Joe Lafiosca
Ovidiu Cristescu
François Dupayrat
Josh Arnold
Rahul Nallappa
negue
Chris Drackett
Matti Salokangas
Andres Garcia
Built and maintained using OpenKit.
No open issues yet, or sync has not completed.