CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-dnd

Drag and Drop for React applications with hooks-based API and TypeScript support

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

drag-sources.mddocs/

Drag Sources

The useDrag hook enables components to act as drag sources, allowing users to drag elements and initiate drag and drop operations.

Capabilities

useDrag Hook

Creates a draggable element with comprehensive configuration options for drag behavior.

/**
 * Hook for making components draggable
 * @param specArg - Drag source specification (object or function)
 * @param deps - Optional dependency array for memoization
 * @returns Tuple of [collected props, drag ref connector, preview ref connector]
 */
function useDrag<DragObject = unknown, DropResult = unknown, CollectedProps = unknown>(
  specArg: FactoryOrInstance<DragSourceHookSpec<DragObject, DropResult, CollectedProps>>,
  deps?: unknown[]
): [CollectedProps, ConnectDragSource, ConnectDragPreview];

interface DragSourceHookSpec<DragObject, DropResult, CollectedProps> {
  /** The type of item being dragged - required */
  type: SourceType;
  /** Item data or factory function - defines what data is available to drop targets */
  item?: DragObject | DragObjectFactory<DragObject>;
  /** Drag source options for visual effects */
  options?: DragSourceOptions;
  /** Preview rendering options */
  previewOptions?: DragPreviewOptions;
  /** Called when drag operation ends */
  end?: (draggedItem: DragObject, monitor: DragSourceMonitor<DragObject, DropResult>) => void;
  /** Determines if dragging is allowed */
  canDrag?: boolean | ((monitor: DragSourceMonitor<DragObject, DropResult>) => boolean);
  /** Custom logic for determining drag state */
  isDragging?: (monitor: DragSourceMonitor<DragObject, DropResult>) => boolean;
  /** Function to collect properties from monitor */
  collect?: (monitor: DragSourceMonitor<DragObject, DropResult>) => CollectedProps;
}

type DragObjectFactory<T> = (monitor: DragSourceMonitor<T>) => T | null;
type FactoryOrInstance<T> = T | (() => T);

Basic Usage:

import React from "react";
import { useDrag } from "react-dnd";

interface DragItem {
  id: string;
  name: string;
}

function DraggableCard({ id, name }: DragItem) {
  const [{ isDragging }, drag] = useDrag({
    type: "card",
    item: { id, name },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  return (
    <div 
      ref={drag} 
      style={{ 
        opacity: isDragging ? 0.5 : 1,
        cursor: 'move'
      }}
    >
      {name}
    </div>
  );
}

Advanced Usage with All Options:

import React, { useState } from "react";
import { useDrag } from "react-dnd";

function AdvancedDraggableItem({ id, data, disabled }) {
  const [dragCount, setDragCount] = useState(0);

  const [collected, drag, preview] = useDrag({
    type: "advanced-item",
    
    // Dynamic item factory
    item: (monitor) => {
      console.log("Drag started");
      return { id, data, timestamp: Date.now() };
    },
    
    // Conditional dragging
    canDrag: !disabled,
    
    // Custom drag state logic
    isDragging: (monitor) => {
      return monitor.getItem()?.id === id;
    },
    
    // Drag end handler
    end: (item, monitor) => {
      setDragCount(prev => prev + 1);
      
      if (monitor.didDrop()) {
        const dropResult = monitor.getDropResult();
        console.log("Item dropped:", dropResult);
      } else {
        console.log("Drag cancelled");
      }
    },
    
    // Visual options
    options: {
      dropEffect: "move"
    },
    
    // Preview options
    previewOptions: {
      captureDraggingState: false,
      anchorX: 0.5,
      anchorY: 0.5
    },
    
    // Collect function
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
      canDrag: monitor.canDrag(),
      itemType: monitor.getItemType(),
      dragOffset: monitor.getClientOffset(),
    }),
  });

  return (
    <div>
      <div ref={drag} style={{ opacity: collected.isDragging ? 0.5 : 1 }}>
        Draggable Item (dragged {dragCount} times)
      </div>
      <div ref={preview}>
        Custom Preview Content
      </div>
    </div>
  );
}

Connector Functions

The useDrag hook returns connector functions for attaching drag functionality to DOM elements.

/** Function to connect DOM elements as drag sources */
type ConnectDragSource = DragElementWrapper<DragSourceOptions>;

/** Function to connect custom drag preview elements */
type ConnectDragPreview = DragElementWrapper<DragPreviewOptions>;

type DragElementWrapper<Options> = (
  elementOrNode: ConnectableElement,
  options?: Options
) => React.ReactElement | null;

type ConnectableElement = React.RefObject<any> | React.ReactElement | Element | null;

Usage Examples:

function CustomConnectorExample() {
  const [{ isDragging }, drag, preview] = useDrag({
    type: "item",
    item: { id: "example" },
    collect: (monitor) => ({ isDragging: monitor.isDragging() })
  });

  return (
    <div>
      {/* Basic drag connector */}
      <button ref={drag}>Drag Handle</button>
      
      {/* Separate preview element */}
      <div ref={preview}>
        This will be shown during drag
      </div>
      
      {/* Connector with options */}
      {drag(
        <div style={{ padding: 10 }}>
          Custom draggable area
        </div>,
        { dropEffect: "copy" }
      )}
    </div>
  );
}

Drag Source Monitor

Monitor interface providing information about the current drag operation.

interface DragSourceMonitor<DragObject = unknown, DropResult = unknown> {
  /** Returns true if dragging is allowed */
  canDrag(): boolean;
  /** Returns true if this component is being dragged */
  isDragging(): boolean;
  /** Returns the type of item being dragged */
  getItemType(): Identifier | null;
  /** Returns the dragged item data */
  getItem<T = DragObject>(): T;
  /** Returns drop result after drop completes */
  getDropResult<T = DropResult>(): T | null;
  /** Returns true if drop was handled by a target */
  didDrop(): boolean;
  /** Returns initial pointer coordinates when drag started */
  getInitialClientOffset(): XYCoord | null;
  /** Returns initial drag source coordinates */
  getInitialSourceClientOffset(): XYCoord | null;
  /** Returns current pointer coordinates */
  getClientOffset(): XYCoord | null;
  /** Returns pointer movement since drag start */
  getDifferenceFromInitialOffset(): XYCoord | null;
  /** Returns projected source coordinates */
  getSourceClientOffset(): XYCoord | null;
  /** Returns IDs of potential drop targets */
  getTargetIds(): Identifier[];
}

Configuration Options

Drag Source Options

interface DragSourceOptions {
  /** Visual drop effect hint ('move', 'copy', etc.) */
  dropEffect?: string;
}

Drag Preview Options

interface DragPreviewOptions {
  /** Whether to immediately capture dragging state */
  captureDraggingState?: boolean;
  /** Horizontal anchor point (0-1) */
  anchorX?: number;
  /** Vertical anchor point (0-1) */
  anchorY?: number;
  /** Horizontal offset from cursor */
  offsetX?: number;
  /** Vertical offset from cursor */
  offsetY?: number;
}

Common Patterns

Conditional Dragging

function ConditionalDrag({ item, canEdit }) {
  const [{ isDragging }, drag] = useDrag({
    type: "item",
    item,
    canDrag: canEdit && item.status !== "locked",
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  return (
    <div 
      ref={canEdit ? drag : null}
      style={{ 
        opacity: isDragging ? 0.5 : 1,
        cursor: canEdit ? 'move' : 'default'
      }}
    >
      {item.name}
    </div>
  );
}

Multi-type Dragging

function MultiTypeDragItem({ item, mode }) {
  const [{ isDragging }, drag] = useDrag({
    type: mode === "copy" ? "copyable-item" : "movable-item",
    item: { ...item, mode },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  return <div ref={drag}>{item.name}</div>;
}

Install with Tessl CLI

npx tessl i tessl/npm-react-dnd

docs

context-provider.md

drag-layer.md

drag-sources.md

drop-targets.md

index.md

utilities.md

tile.json