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-scrollingUp
: Auto-scrolling upwardDown
: 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
- Sortable Component - Component documentation
- SortableItem Component - Item component documentation
- useSortable Hook - Individual item hook
- useSortableList Hook - List management hook
- Draggable Types - Related draggable types