About this animation
This React Native drag and drop list reorders rows inside a single list. Hold a row for 300 milliseconds or drag its grip immediately: the row lifts, tilts with velocity, and opens a gap as it crosses neighboring slots. Edge auto-scroll preserves its position under the finger. Releasing settles into the nearest slot; regrabbing interrupts the spring from its current position. Supply stable item IDs, title and subtitle content, and row dimensions. onOrderChange receives the new array after settling so your application can persist it. This is a reorderable queue rather than a cross-list drag-and-drop framework.
When to use it
- Reorder a playback queue.
- Prioritize a short task list.
- Persist a user-defined row order.
Related guides
A reorderable list with lifted rows and animated insertion space, with configurable items, row height, lifted color, and fonts.
Why this one
Most reorder examples stop at translating one row. This version adds a two-layer depth treatment, velocity-driven tilt, immediate gap opening, edge auto-scroll, an interruptible release, and screen-reader move actions without using React state per frame.
How it works
- A single continuous shared position tracks the dragged row in row units.
- Neighboring rows derive their offsets when that position crosses their midpoint.
- A 300 ms hold starts dragging anywhere; the Lucide grip starts immediately.
- Edge proximity updates the scroll offset on the UI thread while preserving the finger-to-row relationship.
- Release starts at the exact dragged position and uses an overdamped spring with overshoot clamping to reach the nearest slot.
- Rows use absolute UI-thread slots keyed by item ID. The visual order and drag reset change together when the spring finishes; React receives the array afterward, so even a delayed render cannot move a row back to its old slot. Regrabbing the settling row cancels its spring and continues from its current position.
Install
npx expo install expo-image @shopify/react-native-skia react-native-gesture-handler react-native-reanimated react-native-workletsIcons: Lucide (ISC).
Usage
import { LiftReorderList } from './lift-reorder-list';
const songs = [
{ id: 'one', title: 'Paper Moons', subtitle: 'Elio North', trailing: '3:42' },
{ id: 'two', title: 'Violet Hour', subtitle: 'June Arcade', trailing: '4:06' },
];
export function Queue() {
return <LiftReorderList items={songs} onOrderChange={(next) => saveQueue(next)} />;
}<LiftReorderList
items={songs}
rowHeight={68}
rowGap={4}
liftedColor="#211A29"
accentColor="#F472B6"
accessibilityLabel="Playback queue"
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | LiftReorderItem[] | Required | Items displayed in the reorderable list. |
rowHeight | number | 64 | Height of each row. |
rowGap | number | 2 | Gap between rows. |
liftedColor | string | '#1D1823' | Background of a lifted row. |
accentColor | string | '#C084FC' | Accent used for the release highlight. |
textColor | string | '#F7F2FA' | Primary text color. |
mutedColor | string | '#9C92A5' | Secondary text and grip color. |
fontFamily | string | undefined | Typeface for titles and subtitles. |
mediumFontFamily | string | undefined | Medium typeface for row titles. |
monoFontFamily | string | undefined | Monospaced typeface for trailing values. |
onOrderChange | (items: LiftReorderItem[]) => void | undefined | Called after a move settles with the new item order. |
accessibilityLabel | string | 'Reorderable list' | Screen-reader label for the list. |
style | StyleProp<ViewStyle> | undefined | Optional style for the outer container. |
testID | string | undefined | Test identifier applied to the outer container. |
duration | number | 820 | Programmatic reorder movement duration in milliseconds. Spring physics and reduced-motion timing are unchanged. |
The ref exposes move(fromIndex, toIndex) and reset().
Compatibility
Built for Expo SDK 58, React Native 0.88, Reanimated 4.7, Worklets 0.13, and Skia 2.14. Supports iOS, Android, and web. Compatible with Expo Go because every native dependency is included in Expo Go for this SDK.
Accessibility
Each row is an adjustable element with labeled Move up and Move down actions. Hit areas are at least 44 points. Reduced Motion removes scale, tilt, and animated settling; released and programmatic moves update their slots immediately.
FAQ
- How does dragging start and what happens near the edge?
- A 300 ms hold starts a drag on the row; its grip starts immediately. Near an edge, auto-scroll advances the list while preserving the finger-to-row relationship. Release settles the row into its nearest slot.
- How do I save or change the row order?
- Use onOrderChange to persist the new item array after a move settles. The ref offers move(fromIndex, toIndex) and reset(). The component reorders its own rows; cross-list transfers are outside its documented API.
- Can someone reorder without a drag gesture?
- Each row is adjustable with labeled Move up and Move down actions and hit areas of at least 44 points. Reduced Motion removes lift, tilt, and animated settling, applying moves immediately.




