React Native Reanimated DnDReact Native Reanimated DnD
API ReferenceTypes & Interfaces

Context Types

Complete type definitions for context and provider components.

Type Aliases

DropAlignment

Alignment options for positioning dropped items within a droppable area.

type DropAlignment =
  | "center"
  | "top-left"
  | "top-center"
  | "top-right"
  | "center-left"
  | "center-right"
  | "bottom-left"
  | "bottom-center"
  | "bottom-right";

Values

  • center: Center the dropped item (default)
  • top-left: Position at top-left corner
  • top-center: Position at top edge, centered horizontally
  • top-right: Position at top-right corner
  • center-left: Position at left edge, centered vertically
  • center-right: Position at right edge, centered vertically
  • bottom-left: Position at bottom-left corner
  • bottom-center: Position at bottom edge, centered horizontally
  • bottom-right: Position at bottom-right corner

Usage Examples

// Center the dropped item (default)
const centerAlignment: DropAlignment = "center";

// Position at top-left corner
const topLeftAlignment: DropAlignment = "top-left";

// Position at bottom edge, centered horizontally
const bottomCenterAlignment: DropAlignment = "bottom-center";

PositionUpdateListener

Callback function type for position update notifications.

type PositionUpdateListener = () => void;

Description

Function called when position updates are triggered. Used internally by components to recalculate positions after layout changes.

Interfaces

DropOffset

Pixel offset to apply after alignment positioning.

interface DropOffset {
  x: number;
  y: number;
}

Properties

x
  • Type: number
  • Description: Horizontal offset in pixels (positive = right, negative = left)
y
  • Type: number
  • Description: Vertical offset in pixels (positive = down, negative = up)

Usage Examples

// No offset (default)
const noOffset: DropOffset = { x: 0, y: 0 };

// Move 10px right and 5px down from aligned position
const customOffset: DropOffset = { x: 10, y: 5 };

// Move 20px left from aligned position
const leftOffset: DropOffset = { x: -20, y: 0 };

DroppedItemsMap<TData>

Mapping of draggable items to their drop locations.

interface DroppedItemsMap<TData = unknown> {
  [draggableId: string]: {
    droppableId: string;
    data: TData;
  };
}

Properties

  • Key: string - The unique ID of the draggable item
  • Value: Object containing:
    • droppableId: ID of the droppable where the item was dropped
    • data: The data associated with the dropped item

Usage Example

const droppedItems: DroppedItemsMap<TaskData> = {
  "task-1": {
    droppableId: "completed-column",
    data: { id: "task-1", title: "Complete project", status: "done" },
  },
  "task-2": {
    droppableId: "in-progress-column",
    data: { id: "task-2", title: "Review code", status: "in-progress" },
  },
};

DropSlot<TData>

Configuration for a single drop zone.

interface DropSlot<TData = unknown> {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
  onDrop: (data: TData) => void;
  dropAlignment?: DropAlignment;
  dropOffset?: DropOffset;
  capacity?: number;
}

Properties

id
  • Type: string
  • Required: Yes
  • Description: Unique identifier for the drop slot
x
  • Type: number
  • Required: Yes
  • Description: X coordinate of the drop slot
y
  • Type: number
  • Required: Yes
  • Description: Y coordinate of the drop slot
width
  • Type: number
  • Required: Yes
  • Description: Width of the drop slot
height
  • Type: number
  • Required: Yes
  • Description: Height of the drop slot
onDrop
  • Type: (data: TData) => void
  • Required: Yes
  • Description: Callback function called when an item is dropped
dropAlignment
  • Type: DropAlignment
  • Required: No
  • Description: How dropped items should be aligned within this slot
dropOffset
  • Type: DropOffset
  • Required: No
  • Description: Additional pixel offset to apply after alignment
capacity
  • Type: number
  • Required: No
  • Description: Maximum number of items this slot can hold

SlotsContextValue<TData>

The main context value interface providing drag-and-drop functionality.

interface SlotsContextValue<TData = unknown> {
  // Drop zone management
  register: (id: number, slot: DropSlot<TData>) => void;
  unregister: (id: number) => void;
  getSlots: () => Record<number, DropSlot<TData>>;
  isRegistered: (id: number) => boolean;

  // Active state management
  setActiveHoverSlot: (id: number | null) => void;
  activeHoverSlotId: number | null;

  // Position updates
  registerPositionUpdateListener: (
    id: string,
    listener: PositionUpdateListener
  ) => void;
  unregisterPositionUpdateListener: (id: string) => void;
  requestPositionUpdate: () => void;

  // Dropped items management
  registerDroppedItem: (
    draggableId: string,
    droppableId: string,
    itemData: any
  ) => void;
  unregisterDroppedItem: (draggableId: string) => void;
  getDroppedItems: () => DroppedItemsMap<any>;

  // Capacity management
  hasAvailableCapacity: (droppableId: string) => boolean;

  // Event callbacks
  onDragging?: (payload: DraggingPayload) => void;
  onDragStart?: (data: any) => void;
  onDragEnd?: (data: any) => void;
}

Drop Zone Management Methods

register(id, slot)
  • Type: (id: number, slot: DropSlot<TData>) => void
  • Description: Registers a new drop zone with the context
  • Parameters:
    • id: Unique numeric identifier for the drop zone
    • slot: Drop zone configuration object
unregister(id)
  • Type: (id: number) => void
  • Description: Removes a drop zone from the context
  • Parameters:
    • id: Unique identifier of the drop zone to remove
getSlots()
  • Type: () => Record<number, DropSlot<TData>>
  • Description: Returns all currently registered drop zones
  • Returns: Object mapping slot IDs to their configurations
isRegistered(id)
  • Type: (id: number) => boolean
  • Description: Checks if a drop zone is currently registered
  • Parameters:
    • id: Drop zone ID to check
  • Returns: True if the drop zone is registered

Active State Management

setActiveHoverSlot(id)
  • Type: (id: number | null) => void
  • Description: Sets the currently active (hovered) drop zone
  • Parameters:
    • id: ID of the active drop zone, or null for none
activeHoverSlotId
  • Type: number | null
  • Description: The ID of the currently active drop zone

Position Update Methods

registerPositionUpdateListener(id, listener)
  • Type: (id: string, listener: PositionUpdateListener) => void
  • Description: Registers a listener for position updates
  • Parameters:
    • id: Unique identifier for the listener
    • listener: Callback function to invoke on updates
unregisterPositionUpdateListener(id)
  • Type: (id: string) => void
  • Description: Removes a position update listener
  • Parameters:
    • id: Unique identifier of the listener to remove
requestPositionUpdate()
  • Type: () => void
  • Description: Triggers position updates for all registered listeners

Dropped Items Management

registerDroppedItem(draggableId, droppableId, itemData)
  • Type: (draggableId: string, droppableId: string, itemData: any) => void
  • Description: Records that an item has been dropped in a specific zone
  • Parameters:
    • draggableId: ID of the draggable item
    • droppableId: ID of the drop zone
    • itemData: Data associated with the dropped item
unregisterDroppedItem(draggableId)
  • Type: (draggableId: string) => void
  • Description: Removes a dropped item from the registry
  • Parameters:
    • draggableId: ID of the draggable item to remove
getDroppedItems()
  • Type: () => DroppedItemsMap<any>
  • Description: Returns the current mapping of dropped items

Capacity Management

hasAvailableCapacity(droppableId)
  • Type: (droppableId: string) => boolean
  • Description: Checks if a drop zone has available capacity
  • Parameters:
    • droppableId: ID of the drop zone to check
  • Returns: True if the drop zone can accept more items

Event Callbacks

onDragging
  • Type: (payload: DraggingPayload) => void
  • Required: No
  • Description: Global callback fired during drag operations
onDragStart
  • Type: (data: any) => void
  • Required: No
  • Description: Global callback fired when any drag operation starts
onDragEnd
  • Type: (data: any) => void
  • Required: No
  • Description: Global callback fired when any drag operation ends

DropProviderProps

Props for the DropProvider component.

interface DropProviderProps {
  children: ReactNode;
  onLayoutUpdateComplete?: () => void;
  onDroppedItemsUpdate?: (droppedItems: DroppedItemsMap) => void;
  onDragging?: (payload: DraggingPayload) => void;
  onDragStart?: (data: any) => void;
  onDragEnd?: (data: any) => void;
}

Properties

children
  • Type: ReactNode
  • Required: Yes
  • Description: The child components that will have access to the drag-and-drop context
onLayoutUpdateComplete
  • Type: () => void
  • Required: No
  • Description: Callback fired when layout updates are complete. Useful for triggering additional UI updates after position recalculations.
onDroppedItemsUpdate
  • Type: (droppedItems: DroppedItemsMap) => void
  • Required: No
  • Description: Callback fired when the dropped items mapping changes. Provides access to the current state of which items are dropped where.
onDragging
  • Type: (payload: DraggingPayload) => void
  • Required: No
  • Description: Global callback fired during drag operations. Receives position updates for all draggable items.
onDragStart
  • Type: (data: any) => void
  • Required: No
  • Description: Global callback fired when any drag operation starts.
onDragEnd
  • Type: (data: any) => void
  • Required: No
  • Description: Global callback fired when any drag operation ends.

DropProviderRef

Imperative handle interface for the DropProvider component.

interface DropProviderRef {
  requestPositionUpdate: () => void;
  getDroppedItems: () => DroppedItemsMap;
}

Methods

requestPositionUpdate()
  • Type: () => void
  • Description: Manually trigger a position update for all registered droppables and draggables. Useful after layout changes or when positions may have become stale.
getDroppedItems()
  • Type: () => DroppedItemsMap
  • Description: Get the current mapping of dropped items.
  • Returns: Object mapping draggable IDs to their drop information

DraggingPayload

Payload object for drag event callbacks.

interface DraggingPayload {
  x: number; // Original X position
  y: number; // Original Y position
  tx: number; // Current X translation
  ty: number; // Current Y translation
  itemData: any; // Data associated with the draggable item
}

Properties

  • x: Original X position of the item
  • y: Original Y position of the item
  • tx: Current X translation from original position
  • ty: Current Y translation from original position
  • itemData: The data associated with the draggable item

Usage Examples

Basic Context Usage

import { useContext } from "react";
import { SlotsContext } from "react-native-reanimated-dnd";

function MyComponent() {
  const context = useContext(SlotsContext);

  if (!context) {
    throw new Error("Component must be used within a DropProvider");
  }

  const { getSlots, getDroppedItems, activeHoverSlotId } = context;

  return (
    <View>
      <Text>Active Slots: {Object.keys(getSlots()).length}</Text>
      <Text>Dropped Items: {Object.keys(getDroppedItems()).length}</Text>
      <Text>Active Hover: {activeHoverSlotId || "None"}</Text>
    </View>
  );
}

Custom Hook for Context Access

import { useContext } from "react";
import { SlotsContext, SlotsContextValue } from "react-native-reanimated-dnd";

function useDragDropContext<TData = unknown>(): SlotsContextValue<TData> {
  const context = useContext(SlotsContext);

  if (!context) {
    throw new Error("useDragDropContext must be used within a DropProvider");
  }

  return context as SlotsContextValue<TData>;
}

// Usage
function TaskBoard() {
  const { getDroppedItems, hasAvailableCapacity } =
    useDragDropContext<TaskData>();

  const droppedItems = getDroppedItems();
  const canDrop = hasAvailableCapacity("todo-column");

  return (
    <View>
      <Text>Tasks: {Object.keys(droppedItems).length}</Text>
      <Text>Can Drop: {canDrop ? "Yes" : "No"}</Text>
    </View>
  );
}

Provider with All Callbacks

import {
  DropProvider,
  DroppedItemsMap,
  DraggingPayload,
} from "react-native-reanimated-dnd";
import { GestureHandlerRootView } from "react-native-gesture-handler";

function App() {
  const handleDroppedItemsUpdate = (
    droppedItems: DroppedItemsMap<TaskData>
  ) => {
    console.log("Dropped items updated:", droppedItems);
    // Sync with your state management
    updateGlobalState(droppedItems);
  };

  const handleDragStart = (data: TaskData) => {
    console.log("Drag started:", data.title);
    hapticFeedback();
    setDragIndicator(true);
  };

  const handleDragEnd = (data: TaskData) => {
    console.log("Drag ended:", data.title);
    setDragIndicator(false);
  };

  const handleDragging = ({ x, y, tx, ty, itemData }: DraggingPayload) => {
    const currentX = x + tx;
    const currentY = y + ty;
    updateDragPosition(currentX, currentY, itemData);
  };

  return (
    <GestureHandlerRootView style={styles.container}>
      <DropProvider
        onDroppedItemsUpdate={handleDroppedItemsUpdate}
        onDragStart={handleDragStart}
        onDragEnd={handleDragEnd}
        onDragging={handleDragging}
        onLayoutUpdateComplete={() => console.log("Layout updated")}
      >
        <TaskBoard />
      </DropProvider>
    </GestureHandlerRootView>
  );
}

Advanced Context Integration

function AdvancedContextConsumer() {
  const context = useDragDropContext<TaskData>();
  const [analytics, setAnalytics] = useState({
    totalSlots: 0,
    activeSlots: 0,
    droppedItems: 0,
    capacityUtilization: 0,
  });

  useEffect(() => {
    const updateAnalytics = () => {
      const slots = context.getSlots();
      const droppedItems = context.getDroppedItems();

      const totalSlots = Object.keys(slots).length;
      const activeSlots = context.activeHoverSlotId ? 1 : 0;
      const totalDroppedItems = Object.keys(droppedItems).length;

      // Calculate capacity utilization
      const totalCapacity = Object.values(slots).reduce(
        (sum, slot) => sum + (slot.capacity || 1),
        0
      );
      const capacityUtilization =
        totalCapacity > 0 ? (totalDroppedItems / totalCapacity) * 100 : 0;

      setAnalytics({
        totalSlots,
        activeSlots,
        droppedItems: totalDroppedItems,
        capacityUtilization,
      });
    };

    const interval = setInterval(updateAnalytics, 1000);
    return () => clearInterval(interval);
  }, [context]);

  return (
    <View style={styles.analytics}>
      <Text>Total Drop Zones: {analytics.totalSlots}</Text>
      <Text>Active Zones: {analytics.activeSlots}</Text>
      <Text>Dropped Items: {analytics.droppedItems}</Text>
      <Text>Capacity Usage: {analytics.capacityUtilization.toFixed(1)}%</Text>
    </View>
  );
}

Custom Drop Slot Registration

function CustomDroppable({
  onDrop,
  children,
}: {
  onDrop: (data: TaskData) => void;
  children: ReactNode;
}) {
  const context = useDragDropContext<TaskData>();
  const viewRef = useRef<View>(null);
  const slotId = useRef(Math.random());

  useEffect(() => {
    const measureAndRegister = () => {
      viewRef.current?.measure((x, y, width, height, pageX, pageY) => {
        const slot: DropSlot<TaskData> = {
          id: "custom-droppable",
          x: pageX,
          y: pageY,
          width,
          height,
          onDrop,
          dropAlignment: "center",
          dropOffset: { x: 0, y: 0 },
          capacity: 5,
        };

        context.register(slotId.current, slot);
      });
    };

    measureAndRegister();
    return () => context.unregister(slotId.current);
  }, [context, onDrop]);

  return (
    <View ref={viewRef} onLayout={measureAndRegister}>
      {children}
    </View>
  );
}

Position Update Listener

function ResponsiveComponent() {
  const { registerPositionUpdateListener, unregisterPositionUpdateListener } =
    useDragDropContext();
  const listenerId = useRef(`listener-${Math.random()}`);

  useEffect(() => {
    const handlePositionUpdate = () => {
      // Re-measure and update positions
      console.log("Position update triggered");
      remeasureComponent();
    };

    registerPositionUpdateListener(listenerId.current, handlePositionUpdate);

    return () => {
      unregisterPositionUpdateListener(listenerId.current);
    };
  }, [registerPositionUpdateListener, unregisterPositionUpdateListener]);

  return <View>{/* Your component */}</View>;
}

Capacity Management

function CapacityManager() {
  const { hasAvailableCapacity, getDroppedItems } = useDragDropContext();
  const [capacityStatus, setCapacityStatus] = useState<Record<string, boolean>>(
    {}
  );

  const dropZones = ["todo", "in-progress", "done"];

  useEffect(() => {
    const checkCapacities = () => {
      const status: Record<string, boolean> = {};
      dropZones.forEach((zoneId) => {
        status[zoneId] = hasAvailableCapacity(zoneId);
      });
      setCapacityStatus(status);
    };

    checkCapacities();
    const interval = setInterval(checkCapacities, 1000);
    return () => clearInterval(interval);
  }, [hasAvailableCapacity]);

  return (
    <View>
      {dropZones.map((zoneId) => (
        <View key={zoneId} style={styles.capacityIndicator}>
          <Text>{zoneId}</Text>
          <Text
            style={[
              styles.status,
              capacityStatus[zoneId] ? styles.available : styles.full,
            ]}
          >
            {capacityStatus[zoneId] ? "Available" : "Full"}
          </Text>
        </View>
      ))}
    </View>
  );
}

TypeScript Support

All context types are fully typed with generic support:

interface TaskData {
  id: string;
  title: string;
  priority: "low" | "medium" | "high";
}

// Typed context usage
const context = useContext(SlotsContext) as SlotsContextValue<TaskData>;

// Typed dropped items
const droppedItems: DroppedItemsMap<TaskData> = context.getDroppedItems();

// Typed drop slot
const slot: DropSlot<TaskData> = {
  id: "task-column",
  x: 0,
  y: 0,
  width: 200,
  height: 400,
  onDrop: (data: TaskData) => {
    // data is properly typed as TaskData
    console.log(`Dropped task: ${data.title}`);
  },
  capacity: 10,
};

See Also