CLI tool
nuclearpasta/react-native-drax avatar
nuclearpasta/react-native-drax

react-native-drax: a declarative drag and drop layer for React Native

A drag-and-drop system for React Native

715 stars93 forksTypeScriptMIT

At a glance

What is it?
react-native-drax puts drag, sortable lists, drag handles and drop acceptance on top of Reanimated 4 and Gesture Handler 3, running receiver detection on the UI thread. It suits teams that want drag behaviour in one place rather than hand-rolled gesture code, with the caveat that cross-list reorder is still labelled experimental and the documentation leans on a comparison table.
Who is it for?
Adopt react-native-drax when your app needs sortable lists, drag handles and guarded drop targets on iOS, Android and Web from one declarative API, and you already run Reanimated 4 with Gesture Handler 3. Skip it if you need battle-tested cross-list reorder, since the project marks that path experimental, or if a web-only canvas is your target.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 21 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Drax replaces in a React Native app

A React Native app that needs to move a row, a tile or a card usually ends up with a hand-written gesture handler, a cached set of measured positions, and animation code that has to stay in step with whatever list renders underneath. react-native-drax packages that work as a declarative layer. You declare which views are draggable, which ones are receivers, and what should happen when a drop lands. The project describes itself as a declarative drag-and-drop framework for React Native written in TypeScript, covering three shapes of interaction: free-form drag and drop, sortable lists and grids, and cross-list reorder. The target reader is a React Native developer who needs drag behaviour that holds up on a physical device, on iOS, Android and Web, rather than a web-only drag implementation grafted through a compatibility layer. The comparison table in the README puts Drax alongside reanimated-dnd and sortables, and that table is the fastest way to see where it draws its line: list agnostic sorting, drop indicators, UI-thread collision detection and 19 callback types are the claims it makes for itself. The smallest complete shape is two views inside a provider, one carrying a payload and the other listening for a drop:

tsx
      <DraxView
        style={{ width: 100, height: 100, backgroundColor: 'blue' }}
        onDragStart={() => console.log('dragging')}
        payload="hello"
      />
      <DraxView
        style={{ width: 100, height: 100, backgroundColor: 'green' }}
        onReceiveDragDrop={({ dragged: { payload } }) => {
          console.log(`received: ${payload}`);
        }}
      />

Receiver detection happens on the UI thread

The performance story rests on one architectural decision. Drax is built on Reanimated 4 and Gesture Handler 3 with what the README calls a UI-thread-first architecture for smooth 60fps interactions. The specific mechanism is hit-testing: the highlights list calls it a UI-thread hit-testing spatial index worklet, used for fast receiver detection. Instead of asking the JavaScript thread which view sits under the finger and waiting for a round trip, the spatial work runs where the gesture is already being processed, and the result arrives as part of the drag phase. That is what makes the drop indicator and hover styles feel immediate rather than delayed by a frame or two. The lifecycle is exposed through 19 event callbacks, so a host app can react to drag start, movement, receiver changes and drop outcomes without reaching into internals. The comparison table credits Drax with UI-thread drag and drop collision while marking reanimated-dnd and sortables as lacking it, and it also notes that a drag handle only starts a drag while the rest of the view keeps scrolling normally. Accessibility is handled at the same level, with auto-generated labels and reduced motion support listed as built-in rather than manual.

Installing Drax alongside Reanimated and Gesture Handler

The package publishes to npm under the name react-native-drax, and the peer dependencies are installed separately because the library expects you to control those versions yourself. This is the install sequence the README gives:

bash
npm install react-native-drax
# or
yarn add react-native-drax

The two gesture libraries come next:

bash
npm install react-native-reanimated react-native-gesture-handler

The version floors are specific and worth checking against what your app already runs. The peer dependency table lists react at >= 18, react-native at >= 0.68, react-native-reanimated at ^4.0.0, and react-native-gesture-handler at >= 2.0.0 with v3 recommended. The comparison table in the same README raises the Reanimated floor for the alternatives, listing >= 4.2 for reanimated-dnd and >= 3 for sortables, so Drax is the loosest of the three on that axis. The package ships TypeScript sources directly for React Native, with a built module and declaration output for other consumers, and the published file list includes the native android, ios and cpp directories alongside the JavaScript bundle, which means the npm tarball carries native code and a podspec. A first run needs a working Reanimated 4 and Gesture Handler 3 setup in the host project before any Drax import will behave, and the repository ships an example app with its own screens and components that is wired for that purpose.

Sorting with DraxList, then swapping the list component

The quickest path to a reorderable list is DraxList, and the first real decision is which list component sits underneath it. The README describes DraxList as list-agnostic, and the example below shows a FlashList passed in through the component prop:

tsx
import { FlashList } from '@shopify/flash-list';

<DraxList
  component={FlashList}
  data={items}
  keyExtractor={(item) => item.id}
  onReorder={({ data }) => setItems(data)}
  renderItem={({ item }) => <ItemCard item={item} />}
  estimatedItemSize={60}
/>

The same shape works with FlatList, LegendList, ScrollView, or any component that follows the list contract, which is the practical payoff of the list agnostic claim. Reordering is driven by the onReorder callback, which hands back the reordered data so you can commit it to your own state. When the defaults are not enough, the composable primitives take over. A hook builds the sortable state, and the container and item components wire it to a list you render yourself:

tsx
  const sortable = useSortableList({
    data: items,
    keyExtractor: (item) => item,
    onReorder: ({ data }) => setItems(data),
  });

The container then passes scrollRef to the list and the item wrapper takes the sortable state plus an index, so a plain FlatList keeps working while Drax handles the drag mechanics underneath. The README states plainly that this pattern works with FlatList, FlashList, LegendList, ScrollView and similar components, and a FlashList branch is the one that matters most because of the estimatedItemSize prop shown above.

Guarding drops with acceptsDrag, drag bounds and collision modes

Once a drag is running, the interesting problems are not about picking a target but about deciding whether the target should accept. Drax addresses this with an acceptsDrag mechanism that lets a receiver accept or reject a drop, which is how a kanban column or a locked slot gets enforced rather than merely styled. The same feature set includes drag bounds, which constrain a dragged view within the area of a parent view, and a set of collision algorithms for hit-testing the dragged item against receivers: center, intersect, or contain. The center mode is the cheapest and most predictable, intersect is the forgiving one where any overlap counts, and contain requires the dragged item to sit fully inside a receiver before a drop is offered. Style feedback is also phase-aware. The README lists hover styles as conditional styles based on drag phase and receiver state, and the comparison table counts 15 drag state styling props plus an inactive state against roughly 5 props plus a hook in sortables. For motion, the README offers animation presets of default, spring, gentle, snappy and none, or a custom configuration, and a snap alignment feature that snaps to a 9-point alignment within receivers. That preset list is a small but real decision surface: a chat reordering list and a tile canvas want very different motion, and the presets cover both without hand-authored timing curves.

Where the documentation runs out

The honest limits show up in three places. First, cross-container and cross-list reorder is marked experimental in the project's own comparison table, with a warning symbol next to it while free-form drag and drop and sortable lists carry plain check marks. A kanban board that moves cards between columns is therefore the flagship use case and the least proven one. Second, the README text available here is cut off mid-example in the drag handle section, and the project routes readers to a separate documentation site and a live example for the rest, so the depth you can verify from the repository alone is limited. The README does not document rollback behaviour, does not explain how to migrate between versions, and does not describe what happens to a drag that is interrupted by an app backgrounding or a system interruption. Third, the comparison table itself is a self-published marketing surface, and it is the only place where Drax is measured against reanimated-dnd and sortables. It claims web support where sortables are marked partial and none for reanimated-dnd, but the reasoning behind those cells is not documented. If a decision depends on the exact behaviour of a receiver rejection or a cross-list handoff, the answer is not in the repository and has to be checked in the official documentation before you build on it.

Maintenance cadence and what the MIT licence covers

The project is not archived, and the last push was on 2026-10-03, which is recent enough to describe the codebase as receiving work rather than being left behind. Releases are sparse by design: v1.0.0 landed on 2026-03-22 and v1.1.0 on 2026-03-23, two stable tags one day apart, after which the activity visible in the history has been commits on the main branch rather than a new version. The package version in package.json still reads 1.1.0, so a consumer installing today gets the March release and the subsequent fixes together, which is a smaller spread than a 1.x library with a long release history usually implies. Licence terms are MIT, which is short enough to read in one sitting and permissive in the ways most teams expect, covering use, modification, distribution and private use, with the requirement that the copyright notice and licence text travel with copies. There is no copyleft obligation, and the MIT terms place no conditions on whether a commercial product ships the library. Legal interpretation of any licence is outside what this repository settles, so the only licence fact to take away is the identifier itself and the file it lives in: LICENSE.md at the repository root. The repository also includes CONTRIBUTING.md, CODE_OF_CONDUCT.md, a CHANGELOG.md, an eslint config, a commitlint configuration and a lefthook.yml, which is a conventional open source housekeeping set rather than anything unusual.

Editorial conclusion

Adopt react-native-drax when your app needs sortable lists, drag handles and guarded drop targets on iOS, Android and Web from one declarative API, and you already run Reanimated 4 with Gesture Handler 3. Skip it if you need battle-tested cross-list reorder, since the project marks that path experimental, or if a web-only canvas is your target. Before committing, check the peer dependency floors (react-native-reanimated ^4.0.0, react-native-gesture-handler >= 2.0.0) against the version of Reanimated already in your app, then read the acceptsDrag and drag bounds behaviour in the official documentation at nuclearpasta.com/react-native-drax.

Frequently asked questions

What is react-native-drax used for?

It is a declarative drag-and-drop framework for React Native written in TypeScript, covering free-form drag and drop, sortable lists and grids, and cross-list reorder. It runs on iOS, Android and Web.

Which peer dependencies does react-native-drax need?

The peer dependency table lists react at >= 18, react-native at >= 0.68, react-native-reanimated at ^4.0.0, and react-native-gesture-handler at >= 2.0.0 with v3 recommended. You install the gesture libraries separately with npm install react-native-reanimated react-native-gesture-handler.

Can react-native-drax sort a FlashList or any other list component?

Yes. DraxList is list-agnostic and you can pass any list component through the component prop, for example component={FlashList}. The composable pattern with useSortableList, SortableContainer and SortableItem works with FlatList, FlashList, LegendList, ScrollView and similar components.

Does react-native-drax support moving items between two different lists?

Cross-container and cross-list reorder is listed as experimental in the project comparison table, while free-form drag and drop and sortable lists are marked as supported. A cross-list kanban board should be treated as an area of risk rather than a settled feature.

How does react-native-drax decide which view should receive a drop?

Receiver detection uses a spatial index worklet on the UI thread, and the available collision algorithms are center, intersect and contain. A receiver can also refuse a drop through the acceptsDrag mechanism.

Official sources

  1. License: MIT
  2. nuclearpasta/react-native-drax on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nuclearpasta-react-native-drax.svg)](https://hysenlabs.com/projects/nuclearpasta-react-native-drax)

Was this useful?