React Native Reanimated DnDReact Native Reanimated DnD
API ReferenceTypes & Interfaces

Droppable Types

Complete type definitions for droppable components and hooks.

Interfaces

UseDroppableOptions<TData>

Configuration options for the useDroppable hook.

interface UseDroppableOptions<TData = unknown> {
  onDrop: (data: TData) => void;
  dropDisabled?: boolean;
  onActiveChange?: (isActive: boolean) => void;
  dropAlignment?: DropAlignment;
  dropOffset?: DropOffset;
  activeStyle?: StyleProp<ViewStyle>;
  droppableId?: string;
  capacity?: number;
}

Properties

onDrop
  • Type: (data: TData) => void
  • Required: Yes
  • Description: Callback function fired when an item is successfully dropped on this droppable. This is where you handle the drop logic for your application.
const handleDrop = (data: TaskData) => {
  console.log("Task dropped:", data.name);
  moveTaskToColumn(data.id, "completed");
  showNotification(`${data.name} completed!`);
};
dropDisabled
  • Type: boolean
  • Default: false
  • Description: Whether this droppable is disabled. When true, items cannot be dropped here. Useful for conditionally enabling/disabling drop functionality.
const isDisabled = user.role !== "admin";

const { viewProps } = useDroppable({
  onDrop: handleDrop,
  dropDisabled: isDisabled,
});
onActiveChange
  • Type: (isActive: boolean) => void
  • Required: No
  • Description: Callback fired when the active state of this droppable changes. Active state indicates whether a draggable item is currently hovering over this droppable.
const handleActiveChange = (isActive: boolean) => {
  if (isActive) {
    playHoverSound();
    setHighlighted(true);
  } else {
    setHighlighted(false);
  }
};
dropAlignment
  • Type: DropAlignment
  • Default: "center"
  • Description: How dropped items should be aligned within this droppable area.

Available alignments:

  • center: Center the item within the droppable (default)
  • top-left: Align to top-left corner
  • top-center: Align to top edge, centered horizontally
  • top-right: Align to top-right corner
  • center-left: Align to left edge, centered vertically
  • center-right: Align to right edge, centered vertically
  • bottom-left: Align to bottom-left corner
  • bottom-center: Align to bottom edge, centered horizontally
  • bottom-right: Align to bottom-right corner
// Items dropped here will snap to the top-left corner
const { viewProps } = useDroppable({
  onDrop: handleDrop,
  dropAlignment: "top-left",
});
dropOffset
  • Type: DropOffset
  • Required: No
  • Description: Additional pixel offset to apply after alignment. Useful for fine-tuning the exact position where items are dropped.
// Drop items 10px to the right and 5px down from the center
const { viewProps } = useDroppable({
  onDrop: handleDrop,
  dropAlignment: "center",
  dropOffset: { x: 10, y: 5 },
});
activeStyle
  • Type: StyleProp<ViewStyle>
  • Required: No
  • Description: Style to apply when a draggable item is hovering over this droppable. This provides visual feedback to users about valid drop targets.
const activeStyle = {
  backgroundColor: "rgba(0, 255, 0, 0.2)",
  borderColor: "#00ff00",
  borderWidth: 2,
  transform: [{ scale: 1.05 }],
};

const { viewProps } = useDroppable({
  onDrop: handleDrop,
  activeStyle,
});
droppableId
  • Type: string
  • Required: No
  • Description: Unique identifier for this droppable. If not provided, one will be generated automatically. Used for tracking which droppable items are dropped on.
const { viewProps } = useDroppable({
  droppableId: "todo-column",
  onDrop: handleDrop,
});
capacity
  • Type: number
  • Default: 1
  • Description: Maximum number of items that can be dropped on this droppable. When capacity is reached, additional items cannot be dropped here.
// Allow up to 5 items in this drop zone
const { viewProps } = useDroppable({
  onDrop: handleDrop,
  capacity: 5,
});

// Unlimited capacity
const { viewProps } = useDroppable({
  onDrop: handleDrop,
  capacity: Infinity,
});

UseDroppableReturn

Return value from the useDroppable hook.

interface UseDroppableReturn {
  viewProps: {
    onLayout: (event: LayoutChangeEvent) => void;
    style?: StyleProp<ViewStyle>;
  };
  isActive: boolean;
  activeStyle?: StyleProp<ViewStyle>;
  animatedViewRef: ReturnType<typeof useAnimatedRef<Animated.View>>;
}

Properties

viewProps
  • Type: { onLayout: (event: LayoutChangeEvent) => void; style?: StyleProp<ViewStyle>; }
  • Description: Props to spread on the view that will act as a drop zone. Contains layout handler and conditional active styling.
isActive
  • Type: boolean
  • Description: Whether a draggable item is currently hovering over this droppable. Useful for conditional rendering or additional visual feedback.
activeStyle
  • Type: StyleProp<ViewStyle>
  • Description: The active style that was passed in options. Useful for external styling logic.
animatedViewRef
  • Type: ReturnType<typeof useAnimatedRef<Animated.View>>
  • Description: Animated ref for the droppable view. Used internally for measurements.

DroppableProps<TData>

Props for the Droppable component.

interface DroppableProps<TData = unknown> extends UseDroppableOptions<TData> {
  style?: StyleProp<ViewStyle>;
  children: React.ReactNode;
}

Properties

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

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 item within the droppable (default)
  • top-left: Align to top-left corner
  • top-center: Align to top edge, centered horizontally
  • top-right: Align to top-right corner
  • center-left: Align to left edge, centered vertically
  • center-right: Align to right edge, centered vertically
  • bottom-left: Align to bottom-left corner
  • bottom-center: Align to bottom edge, centered horizontally
  • bottom-right: Align to bottom-right corner

DropOffset

Pixel offset configuration for fine-tuning drop positioning.

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

Properties

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

Usage Examples

Basic Droppable

import { useDroppable } from "react-native-reanimated-dnd";

interface TaskData {
  id: string;
  title: string;
  status: "todo" | "in-progress" | "done";
}

function DropZone({ status }: { status: string }) {
  const { viewProps, isActive } = useDroppable({
    onDrop: (data: TaskData) => {
      console.log(`Task ${data.title} dropped in ${status}`);
      updateTaskStatus(data.id, status);
    },
    droppableId: `${status}-column`,
  });

  return (
    <View {...viewProps} style={[styles.dropZone, isActive && styles.active]}>
      <Text>{status.toUpperCase()}</Text>
      {isActive && <Text>Drop here!</Text>}
    </View>
  );
}

Droppable with Visual Feedback

function VisualDropZone() {
  const activeStyle = {
    backgroundColor: "rgba(34, 197, 94, 0.2)",
    borderColor: "#22c55e",
    borderWidth: 2,
    borderStyle: "dashed" as const,
    transform: [{ scale: 1.02 }],
  };

  const { viewProps, isActive } = useDroppable({
    onDrop: (data) => handleDrop(data),
    activeStyle,
    onActiveChange: (active) => {
      if (active) {
        hapticFeedback();
        playSound("hover");
      }
    },
  });

  return (
    <Animated.View {...viewProps} style={styles.dropZone}>
      <Icon
        name={isActive ? "check-circle" : "plus-circle"}
        size={24}
        color={isActive ? "#22c55e" : "#6b7280"}
      />
      <Text style={[styles.text, isActive && styles.activeText]}>
        {isActive ? "Release to drop" : "Drop items here"}
      </Text>
    </Animated.View>
  );
}

Capacity-Limited Droppable

function LimitedDropZone({ maxItems = 3 }: { maxItems?: number }) {
  const [droppedItems, setDroppedItems] = useState<TaskData[]>([]);
  const isFull = droppedItems.length >= maxItems;

  const { viewProps, isActive } = useDroppable({
    onDrop: (data: TaskData) => {
      if (!isFull) {
        setDroppedItems((prev) => [...prev, data]);
        showToast(`${data.title} added`);
      } else {
        showError("Drop zone is full!");
      }
    },
    capacity: maxItems,
    dropDisabled: isFull,
    activeStyle: {
      backgroundColor: isFull
        ? "rgba(239, 68, 68, 0.2)"
        : "rgba(34, 197, 94, 0.2)",
      borderColor: isFull ? "#ef4444" : "#22c55e",
    },
  });

  return (
    <View {...viewProps} style={[styles.dropZone, isFull && styles.fullZone]}>
      <Text>
        Items: {droppedItems.length}/{maxItems}
      </Text>
      {droppedItems.map((item) => (
        <Text key={item.id} style={styles.item}>
          {item.title}
        </Text>
      ))}
      {isFull && <Text style={styles.fullText}>Zone Full</Text>}
    </View>
  );
}

Aligned Droppable

function AlignedDropZones() {
  const alignments: DropAlignment[] = [
    "top-left",
    "top-center",
    "top-right",
    "center-left",
    "center",
    "center-right",
    "bottom-left",
    "bottom-center",
    "bottom-right",
  ];

  return (
    <View style={styles.grid}>
      {alignments.map((alignment) => {
        const { viewProps } = useDroppable({
          onDrop: (data) => console.log(`Dropped at ${alignment}:`, data),
          dropAlignment: alignment,
          dropOffset: { x: 5, y: 5 }, // Small offset for visual clarity
        });

        return (
          <View key={alignment} {...viewProps} style={styles.alignedZone}>
            <Text style={styles.alignmentLabel}>{alignment}</Text>
          </View>
        );
      })}
    </View>
  );
}

File Upload Droppable

interface FileData {
  id: string;
  name: string;
  size: number;
  type: string;
}

function FileUploadZone() {
  const [uploadedFiles, setUploadedFiles] = useState<FileData[]>([]);

  const { viewProps, isActive } = useDroppable({
    onDrop: (file: FileData) => {
      setUploadedFiles((prev) => [...prev, file]);
      uploadFile(file);
    },
    capacity: 10,
    activeStyle: {
      backgroundColor: "rgba(59, 130, 246, 0.1)",
      borderColor: "#3b82f6",
      borderWidth: 2,
      borderStyle: "dashed" as const,
    },
    onActiveChange: (active) => {
      if (active) {
        setDropHint("Release to upload");
      } else {
        setDropHint("Drag files here");
      }
    },
  });

  return (
    <View
      {...viewProps}
      style={[styles.uploadZone, isActive && styles.activeUpload]}
    >
      <Icon
        name="cloud-upload"
        size={48}
        color={isActive ? "#3b82f6" : "#6b7280"}
      />
      <Text style={styles.uploadText}>
        {isActive ? "Release to upload" : "Drag files here"}
      </Text>
      <Text style={styles.fileCount}>
        {uploadedFiles.length}/10 files uploaded
      </Text>

      {uploadedFiles.map((file) => (
        <View key={file.id} style={styles.fileItem}>
          <Text>{file.name}</Text>
          <Text style={styles.fileSize}>{formatFileSize(file.size)}</Text>
        </View>
      ))}
    </View>
  );
}

Conditional Droppable

function ConditionalDropZone({ allowedTypes }: { allowedTypes: string[] }) {
  const { viewProps, isActive } = useDroppable({
    onDrop: (data: TaskData) => {
      if (allowedTypes.includes(data.type)) {
        handleValidDrop(data);
      } else {
        showError(`${data.type} items not allowed here`);
      }
    },
    onActiveChange: (active) => {
      // Check if the hovering item is valid
      const hoveringItem = getCurrentHoveringItem();
      if (active && hoveringItem) {
        const isValid = allowedTypes.includes(hoveringItem.type);
        setValidDrop(isValid);
      }
    },
    activeStyle: {
      backgroundColor: validDrop
        ? "rgba(34, 197, 94, 0.2)"
        : "rgba(239, 68, 68, 0.2)",
      borderColor: validDrop ? "#22c55e" : "#ef4444",
    },
  });

  return (
    <View {...viewProps} style={styles.conditionalZone}>
      <Text>Accepts: {allowedTypes.join(", ")}</Text>
      {isActive && (
        <Text
          style={[styles.feedback, validDrop ? styles.valid : styles.invalid]}
        >
          {validDrop ? "Valid drop target" : "Invalid item type"}
        </Text>
      )}
    </View>
  );
}

See Also