Skip to main content

Sortable Types

Complete type definitions for sortable components and hooks.

Enums

ScrollDirection

Represents the auto-scroll direction during drag operations.

enum ScrollDirection {
None = "none",
Up = "up",
Down = "down",
}

Values

  • None: No auto-scrolling
  • Up: Auto-scrolling upward
  • Down: Auto-scrolling downward

Interfaces

UseSortableOptions<T>

Configuration options for the useSortable hook.

interface UseSortableOptions<T> {
id: string;
positions: SharedValue<{ [id: string]: number }>;
lowerBound: SharedValue<number>;
autoScrollDirection: SharedValue<ScrollDirection>;
itemsCount: number;
itemHeight: number;
containerHeight?: number;
onMove?: (id: string, from: number, to: number) => void;
onDragStart?: (id: string, position: number) => void;
onDrop?: (id: string, position: number) => void;
onDragging?: (id: string, overItemId: string | null, yPosition: number) => void;
children?: React.ReactNode;
handleComponent?: React.ComponentType<any>;
}

Properties

id
  • Type: string
  • Required: Yes
  • Description: Unique identifier for this sortable item. Must be unique within the sortable list. Used for tracking position changes and managing reordering logic.
const itemId = `task-${task.id}`;
positions
  • Type: SharedValue<{ [id: string]: number }>
  • Required: Yes
  • Description: Shared value containing the current positions of all items in the sortable list. This is typically managed by the parent sortable list component.
// Managed by useSortableList hook
const positions = useSharedValue({
'item-1': 0,
'item-2': 1,
'item-3': 2
});
lowerBound
  • Type: SharedValue<number>
  • Required: Yes
  • Description: Shared value representing the current scroll position (lower bound) of the container. Used for auto-scrolling during drag operations.
autoScrollDirection
  • Type: SharedValue<ScrollDirection>
  • Required: Yes
  • Description: Shared value indicating the current auto-scroll direction. Used to trigger automatic scrolling when dragging near container edges.
itemsCount
  • Type: number
  • Required: Yes
  • Description: Total number of items in the sortable list. Used for boundary calculations and position validation.
itemHeight
  • Type: number
  • Required: Yes
  • Description: Height of each item in pixels. All items must have the same height. Used for position calculations and auto-scrolling.
const ITEM_HEIGHT = 60; // 60px per item
containerHeight
  • Type: number
  • Default: 500
  • Description: Height of the scrollable container in pixels. Used for auto-scroll calculations and determining scroll boundaries.
onMove
  • Type: (id: string, from: number, to: number) => void
  • Required: No
  • Description: Callback fired when an item's position changes within the list. This is called for both the moved item and any items that were displaced.
const handleMove = (id: string, from: number, to: number) => {
console.log(`Item ${id} moved from position ${from} to ${to}`);
// Update your data model
reorderItems(id, from, to);
};
onDragStart
  • Type: (id: string, position: number) => void
  • Required: No
  • Description: Callback fired when dragging starts for this item.
const handleDragStart = (id: string, position: number) => {
console.log(`Started dragging item ${id} from position ${position}`);
setDraggingItem(id);
hapticFeedback();
};
onDrop
  • Type: (id: string, position: number) => void
  • Required: No
  • Description: Callback fired when dragging ends for this item.
const handleDrop = (id: string, position: number) => {
console.log(`Dropped item ${id} at position ${position}`);
setDraggingItem(null);
saveNewOrder();
};
onDragging
  • Type: (id: string, overItemId: string | null, yPosition: number) => void
  • Required: No
  • Description: Callback fired continuously while dragging, providing real-time position updates. Useful for showing visual feedback or updating UI during drag operations.
const handleDragging = (id: string, overItemId: string | null, yPosition: number) => {
if (overItemId) {
console.log(`Item ${id} is hovering over ${overItemId}`);
setHoverTarget(overItemId);
} else {
setHoverTarget(null);
}
};

UseSortableReturn

Return value from the useSortable hook.

interface UseSortableReturn {
animatedStyle: StyleProp<ViewStyle>;
panGestureHandler: any;
isMoving: boolean;
hasHandle: boolean;
}

Properties

animatedStyle
  • Type: StyleProp<ViewStyle>
  • Description: Animated style to apply to the sortable item. Contains position transforms and visual effects for dragging state.
panGestureHandler
  • Type: any
  • Description: Pan gesture handler for drag interactions. Attach this to a PanGestureHandler to enable dragging.
isMoving
  • Type: boolean
  • Description: Whether this item is currently being moved/dragged. Useful for conditional styling or behavior.
hasHandle
  • Type: boolean
  • Description: Whether this sortable item has a handle component. When true, only the handle can initiate dragging. When false, the entire item is draggable.

UseSortableListOptions<TData>

Configuration options for the useSortableList hook.

interface UseSortableListOptions<TData> {
data: TData[];
itemHeight: number;
itemKeyExtractor?: (item: TData, index: number) => string;
}

Properties

data
  • Type: TData[]
  • Required: Yes
  • Description: Array of data items to be rendered as sortable list items. Each item must have an id property for tracking.
const tasks = [
{ id: '1', title: 'Task 1', completed: false },
{ id: '2', title: 'Task 2', completed: true },
{ id: '3', title: 'Task 3', completed: false }
];
itemHeight
  • Type: number
  • Required: Yes
  • Description: Height of each item in pixels. All items must have the same height for proper position calculations and smooth animations.
const ITEM_HEIGHT = 80; // Each list item is 80px tall
itemKeyExtractor
  • Type: (item: TData, index: number) => string
  • Default: (item) => item.id
  • Description: Function to extract a unique key from each data item. If not provided, defaults to using the id property.
// Custom key extractor for items without id property
const keyExtractor = (item, index) => `${item.type}-${item.name}-${index}`;

// Using a compound key
const keyExtractor = (item) => `${item.category}_${item.id}`;

UseSortableListReturn<TData>

Return value from the useSortableList hook.

interface UseSortableListReturn<TData> {
positions: any;
scrollY: any;
autoScroll: any;
scrollViewRef: any;
dropProviderRef: React.RefObject<DropProviderRef>;
handleScroll: any;
handleScrollEnd: () => void;
contentHeight: number;
getItemProps: (item: TData, index: number) => SortableItemProps;
}

Properties

positions
  • Type: SharedValue<{ [id: string]: number }>
  • Description: Shared value containing the current positions of all items. Maps item IDs to their current position indices.
// positions.value might look like:
{
'item-1': 0,
'item-2': 1,
'item-3': 2
}
scrollY
  • Type: SharedValue<number>
  • Description: Shared value tracking the current scroll position. Used for auto-scrolling during drag operations.
autoScroll
  • Type: SharedValue<ScrollDirection>
  • Description: Shared value indicating the current auto-scroll direction. Used to trigger automatic scrolling when dragging near edges.
scrollViewRef
  • Type: React.RefObject<Animated.ScrollView>
  • Description: Animated ref for the scroll view component. Used for programmatic scrolling during drag operations.
dropProviderRef
  • Type: React.RefObject<DropProviderRef>
  • Description: Ref for the drop provider context. Used for triggering position updates after scroll events.
handleScroll
  • Type: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
  • Description: Animated scroll handler to attach to the ScrollView. Tracks scroll position for auto-scroll calculations.
handleScrollEnd
  • Type: () => void
  • Description: Callback to call when scrolling ends. Triggers position recalculation for accurate drop zone detection.
contentHeight
  • Type: number
  • Description: Total height of the scrollable content. Calculated as data.length * itemHeight.
getItemProps
  • Type: (item: TData, index: number) => { id: string; positions: SharedValue<{[id: string]: number}>; lowerBound: SharedValue<number>; autoScrollDirection: SharedValue<ScrollDirection>; itemsCount: number; itemHeight: number; }
  • Description: Helper function to get core props for individual sortable items. Returns the essential props that should be spread onto SortableItem components.
{data.map((item, index) => {
const itemProps = getItemProps(item, index);
// itemProps contains: { id, positions, lowerBound, autoScrollDirection, itemsCount, itemHeight }
return (
<SortableItem
key={itemProps.id}
{...itemProps}
data={item}
onMove={handleMove}
>
<Text>{item.title}</Text>
</SortableItem>
);
})}

SortableItemProps<T>

Props for the SortableItem component.

interface SortableItemProps<T> {
id: string;
data: T;
positions: SharedValue<{ [id: string]: number }>;
lowerBound: SharedValue<number>;
autoScrollDirection: SharedValue<ScrollDirection>;
itemsCount: number;
itemHeight: number;
containerHeight?: number;
children: ReactNode;
style?: StyleProp<ViewStyle>;
animatedStyle?: StyleProp<ViewStyle>;
onMove?: (id: string, from: number, to: number) => void;
onDragStart?: (id: string, position: number) => void;
onDrop?: (id: string, position: number) => void;
onDragging?: (id: string, overItemId: string | null, yPosition: number) => void;
}

Properties

id
  • Type: string
  • Required: Yes
  • Description: Unique identifier for this sortable item.
data
  • Type: T
  • Required: Yes
  • Description: Data associated with this sortable item.
positions
  • Type: SharedValue<{ [id: string]: number }>
  • Required: Yes
  • Description: Shared value containing positions of all items in the list.
lowerBound
  • Type: SharedValue<number>
  • Required: Yes
  • Description: Shared value representing the current scroll position.
autoScrollDirection
  • Type: SharedValue<ScrollDirection>
  • Required: Yes
  • Description: Shared value indicating the current auto-scroll direction.
itemsCount
  • Type: number
  • Required: Yes
  • Description: Total number of items in the sortable list.
itemHeight
  • Type: number
  • Required: Yes
  • Description: Height of each item in pixels.
containerHeight
  • Type: number
  • Required: No
  • Description: Height of the scrollable container (optional).
children
  • Type: ReactNode
  • Required: Yes
  • Description: Child components to render inside the sortable item.
style
  • Type: StyleProp<ViewStyle>
  • Required: No
  • Description: Style to apply to the item container.
animatedStyle
  • Type: StyleProp<ViewStyle>
  • Required: No
  • Description: Additional animated style to apply.

SortableProps<TData>

Props for the Sortable component.

interface SortableProps<TData> {
data: TData[];
renderItem: (props: SortableRenderItemProps<TData>) => ReactNode;
itemHeight: number;
style?: StyleProp<ViewStyle>;
contentContainerStyle?: StyleProp<ViewStyle>;
itemKeyExtractor?: (item: TData, index: number) => string;
}

Properties

data
  • Type: TData[]
  • Required: Yes
  • Description: Array of data items to render as sortable list.
renderItem
  • Type: (props: SortableRenderItemProps<TData>) => ReactNode
  • Required: Yes
  • Description: Function to render each sortable item.
itemHeight
  • Type: number
  • Required: Yes
  • Description: Height of each item in pixels.
style
  • Type: StyleProp<ViewStyle>
  • Required: No
  • Description: Style to apply to the scroll view.
contentContainerStyle
  • Type: StyleProp<ViewStyle>
  • Required: No
  • Description: Style to apply to the scroll view content container.
itemKeyExtractor
  • Type: (item: TData, index: number) => string
  • Required: No
  • Description: Function to extract unique key from each item.

SortableRenderItemProps<TData>

Props passed to the renderItem function in Sortable component.

interface SortableRenderItemProps<TData> {
item: TData;
index: number;
id: string;
positions: SharedValue<{ [id: string]: number }>;
lowerBound: SharedValue<number>;
autoScrollDirection: SharedValue<ScrollDirection>;
itemsCount: number;
itemHeight: number;
}

Properties

item
  • Type: TData
  • Description: The data item being rendered.
index
  • Type: number
  • Description: Index of the item in the original data array.
id
  • Type: string
  • Description: Unique identifier for this item.
positions
  • Type: SharedValue<{ [id: string]: number }>
  • Description: Shared value containing positions of all items.
lowerBound
  • Type: SharedValue<number>
  • Description: Shared value representing the current scroll position.
autoScrollDirection
  • Type: SharedValue<ScrollDirection>
  • Description: Shared value indicating the current auto-scroll direction.
itemsCount
  • Type: number
  • Description: Total number of items in the list.
itemHeight
  • Type: number
  • Description: Height of each item in pixels.

SortableContextValue

Context value for sortable components (used internally).

interface SortableContextValue {
panGestureHandler: any;
}

SortableHandleProps

Props for the SortableHandle component.

interface SortableHandleProps {
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
}

Properties

children
  • Type: React.ReactNode
  • Required: Yes
  • Description: The content to render inside the handle.
style
  • Type: StyleProp<ViewStyle>
  • Required: No
  • Description: Optional style to apply to the handle.

Usage Examples

Basic Sortable List

import { useSortableList, SortableItem } from 'react-native-reanimated-dnd';

interface Task {
id: string;
title: string;
completed: boolean;
}

function TaskList() {
const [tasks, setTasks] = useState<Task[]>([
{ id: '1', title: 'Learn React Native', completed: false },
{ id: '2', title: 'Build an app', completed: false },
{ id: '3', title: 'Deploy to store', completed: false }
]);

const {
positions,
scrollY,
autoScroll,
scrollViewRef,
handleScroll,
handleScrollEnd,
contentHeight,
getItemProps
} = useSortableList({
data: tasks,
itemHeight: 80
});

return (
<Animated.ScrollView
ref={scrollViewRef}
onScroll={handleScroll}
onMomentumScrollEnd={handleScrollEnd}
scrollEventThrottle={16}
style={styles.container}
>
<View style={{ height: contentHeight }}>
{tasks.map((task, index) => {
const itemProps = getItemProps(task, index);
return (
<SortableItem
key={itemProps.id}
{...itemProps}
onMove={(id, from, to) => {
// Update task order
const newTasks = [...tasks];
const [movedTask] = newTasks.splice(from, 1);
newTasks.splice(to, 0, movedTask);
setTasks(newTasks);
}}
>
<TaskCard task={task} />
</SortableItem>
);
})}
</View>
</Animated.ScrollView>
);
}

Sortable with Handle

import { SortableItem, SortableHandle } from 'react-native-reanimated-dnd';

function TaskCard({ task }: { task: Task }) {
return (
<View style={styles.taskCard}>
<SortableHandle style={styles.handle}>
<Icon name="drag-handle" size={20} color="#666" />
</SortableHandle>

<View style={styles.content}>
<Text style={styles.title}>{task.title}</Text>
<Text style={styles.status}>
{task.completed ? 'Completed' : 'Pending'}
</Text>
</View>

<TouchableOpacity onPress={() => toggleTask(task.id)}>
<Icon
name={task.completed ? "check-circle" : "circle"}
size={24}
color={task.completed ? "#22c55e" : "#d1d5db"}
/>
</TouchableOpacity>
</View>
);
}

Advanced Sortable with Callbacks

function AdvancedTaskList() {
const [tasks, setTasks] = useState<Task[]>(initialTasks);
const [draggedItem, setDraggedItem] = useState<string | null>(null);
const [analytics, setAnalytics] = useState({
totalMoves: 0,
averageMoveDistance: 0
});

const { getItemProps, ...listProps } = useSortableList({
data: tasks,
itemHeight: 80,
itemKeyExtractor: (item) => `task-${item.id}`
});

const handleMove = useCallback((id: string, from: number, to: number) => {
// Update analytics
const distance = Math.abs(to - from);
setAnalytics(prev => ({
totalMoves: prev.totalMoves + 1,
averageMoveDistance: (prev.averageMoveDistance + distance) / 2
}));

// Update task order
setTasks(prev => {
const newTasks = [...prev];
const [movedTask] = newTasks.splice(from, 1);
newTasks.splice(to, 0, movedTask);
return newTasks;
});

// Save to backend
saveTaskOrder(tasks);
}, [tasks]);

const handleDragStart = useCallback((id: string, position: number) => {
setDraggedItem(id);
hapticFeedback();
console.log(`Started dragging task ${id} from position ${position}`);
}, []);

const handleDrop = useCallback((id: string, position: number) => {
setDraggedItem(null);
console.log(`Dropped task ${id} at position ${position}`);
}, []);

const handleDragging = useCallback((id: string, overItemId: string | null, yPosition: number) => {
if (overItemId && overItemId !== id) {
// Show visual feedback for the item being hovered over
setHoverTarget(overItemId);
} else {
setHoverTarget(null);
}
}, []);

return (
<View style={styles.container}>
<View style={styles.analytics}>
<Text>Total Moves: {analytics.totalMoves}</Text>
<Text>Avg Distance: {analytics.averageMoveDistance.toFixed(1)}</Text>
</View>

<Animated.ScrollView {...listProps}>
<View style={{ height: listProps.contentHeight }}>
{tasks.map((task, index) => {
const itemProps = getItemProps(task, index);
const isDragging = draggedItem === task.id;
const isHovered = hoverTarget === task.id;

return (
<SortableItem
key={itemProps.id}
{...itemProps}
onMove={handleMove}
onDragStart={handleDragStart}
onDrop={handleDrop}
onDragging={handleDragging}
style={[
styles.item,
isDragging && styles.draggingItem,
isHovered && styles.hoveredItem
]}
>
<TaskCard task={task} />
</SortableItem>
);
})}
</View>
</Animated.ScrollView>
</View>
);
}

Custom Key Extractor

interface FileItem {
name: string;
type: 'folder' | 'file';
size?: number;
path: string;
}

function FileList() {
const [files, setFiles] = useState<FileItem[]>(fileData);

const { getItemProps, ...listProps } = useSortableList({
data: files,
itemHeight: 60,
// Custom key extractor for items without id property
itemKeyExtractor: (item, index) => `${item.type}-${item.name}-${index}`
});

return (
<Animated.ScrollView {...listProps}>
<View style={{ height: listProps.contentHeight }}>
{files.map((file, index) => {
const itemProps = getItemProps(file, index);
return (
<SortableItem
key={itemProps.id}
{...itemProps}
onMove={(id, from, to) => {
const newFiles = [...files];
const [movedFile] = newFiles.splice(from, 1);
newFiles.splice(to, 0, movedFile);
setFiles(newFiles);
}}
>
<FileCard file={file} />
</SortableItem>
);
})}
</View>
</Animated.ScrollView>
);
}

High-Level Sortable Component

function SimpleSortableList() {
const [items, setItems] = useState(initialItems);

return (
<Sortable
data={items}
itemHeight={70}
renderItem={({ item, id, positions, ...props }) => (
<SortableItem
id={id}
positions={positions}
{...props}
onMove={(id, from, to) => {
const newItems = [...items];
const [movedItem] = newItems.splice(from, 1);
newItems.splice(to, 0, movedItem);
setItems(newItems);
}}
>
<ItemCard item={item} />
</SortableItem>
)}
style={styles.list}
contentContainerStyle={styles.content}
/>
);
}

See Also