Drag and Drop for React applications with hooks-based API and TypeScript support
npx @tessl/cli install tessl/npm-react-dnd@16.0.0React DnD is a comprehensive drag and drop library for React applications that provides a declarative approach to complex drag and drop interactions. Built with TypeScript, this library offers a flexible API through React hooks (useDrag, useDrop, useDragLayer) that enable developers to create sophisticated drag and drop interfaces with minimal boilerplate code.
npm install react-dndimport { DndProvider, useDrag, useDrop, useDragLayer, DndContext, useDragDropManager } from "react-dnd";For CommonJS:
const { DndProvider, useDrag, useDrop, useDragLayer, DndContext, useDragDropManager } = require("react-dnd");import React from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd";
// Note: Backend is a separate package
import { HTML5Backend } from "react-dnd-html5-backend";
// Wrap your app with DndProvider
function App() {
return (
<DndProvider backend={HTML5Backend}>
<DragDropExample />
</DndProvider>
);
}
// Create a draggable item
function DraggableItem({ id, text }) {
const [{ isDragging }, drag] = useDrag({
type: "item",
item: { id, text },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
return (
<div ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
{text}
</div>
);
}
// Create a drop target
function DropTarget() {
const [{ isOver }, drop] = useDrop({
accept: "item",
drop: (item) => console.log("Dropped:", item),
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
});
return (
<div ref={drop} style={{ backgroundColor: isOver ? "lightblue" : "white" }}>
Drop items here
</div>
);
}React DnD is built around several key components:
DndProvider and DndContext manage the drag and drop state across the component treeuseDrag, useDrop, useDragLayer) provide declarative drag and drop functionalityCore context and provider components for setting up React DnD in your application.
function DndProvider<BackendContext, BackendOptions>(
props: DndProviderProps<BackendContext, BackendOptions>
): React.ReactElement;
type DndProviderProps<BackendContext, BackendOptions> =
| {
children?: React.ReactNode;
manager: DragDropManager;
}
| {
backend: BackendFactory;
children?: React.ReactNode;
context?: BackendContext;
options?: BackendOptions;
debugMode?: boolean;
};Hook for making components draggable with comprehensive configuration options.
function useDrag<DragObject = unknown, DropResult = unknown, CollectedProps = unknown>(
specArg: FactoryOrInstance<DragSourceHookSpec<DragObject, DropResult, CollectedProps>>,
deps?: unknown[]
): [CollectedProps, ConnectDragSource, ConnectDragPreview];
interface DragSourceHookSpec<DragObject, DropResult, CollectedProps> {
type: SourceType;
item?: DragObject | DragObjectFactory<DragObject>;
options?: DragSourceOptions;
previewOptions?: DragPreviewOptions;
end?: (draggedItem: DragObject, monitor: DragSourceMonitor<DragObject, DropResult>) => void;
canDrag?: boolean | ((monitor: DragSourceMonitor<DragObject, DropResult>) => boolean);
isDragging?: (monitor: DragSourceMonitor<DragObject, DropResult>) => boolean;
collect?: (monitor: DragSourceMonitor<DragObject, DropResult>) => CollectedProps;
}Hook for making components accept dropped items with flexible handling options.
function useDrop<DragObject = unknown, DropResult = unknown, CollectedProps = unknown>(
specArg: FactoryOrInstance<DropTargetHookSpec<DragObject, DropResult, CollectedProps>>,
deps?: unknown[]
): [CollectedProps, ConnectDropTarget];
interface DropTargetHookSpec<DragObject, DropResult, CollectedProps> {
accept: TargetType;
options?: DropTargetOptions;
drop?: (item: DragObject, monitor: DropTargetMonitor<DragObject, DropResult>) => DropResult | undefined;
hover?: (item: DragObject, monitor: DropTargetMonitor<DragObject, DropResult>) => void;
canDrop?: (item: DragObject, monitor: DropTargetMonitor<DragObject, DropResult>) => boolean;
collect?: (monitor: DropTargetMonitor<DragObject, DropResult>) => CollectedProps;
}Hook for creating custom drag previews and accessing global drag state.
function useDragLayer<CollectedProps, DragObject = any>(
collect: (monitor: DragLayerMonitor<DragObject>) => CollectedProps
): CollectedProps;Low-level hook for accessing the DragDropManager instance directly for advanced use cases.
function useDragDropManager(): DragDropManager;Utility components for advanced drag and drop scenarios.
function DragPreviewImage(props: DragPreviewImageProps): React.ReactElement;
interface DragPreviewImageProps {
connect: ConnectDragPreview;
src: string;
}// Core identifiers and types (from dnd-core)
type Identifier = string | symbol;
type SourceType = string | symbol;
type TargetType = string | symbol | string[] | symbol[];
// Drag and drop managers and contexts
interface DndContextType {
dragDropManager: DragDropManager | undefined;
}
interface DragDropManager {
getMonitor(): DragDropMonitor;
getBackend(): Backend;
getRegistry(): HandlerRegistry;
dispatch(action: any): void;
}
interface HandlerManager {
receiveHandlerId(handlerId: Identifier | null): void;
getHandlerId(): Identifier | null;
}
interface MonitorEventEmitter {
subscribeToStateChange(
fn: () => void,
options?: { handlerIds?: Identifier[] }
): Unsubscribe;
}
type Unsubscribe = () => void;
type BackendFactory<BackendContext = any, BackendOptions = any> = (
manager: DragDropManager,
context?: BackendContext,
options?: BackendOptions
) => Backend;
interface Backend {
setup(): void;
teardown(): void;
connectDragSource(sourceId: any, node: any, options: any): () => void;
connectDragPreview(sourceId: any, node: any, options: any): () => void;
connectDropTarget(targetId: any, node: any, options: any): () => void;
}
interface HandlerRegistry {
addSource(type: string, source: any): Identifier;
addTarget(type: string, target: any): Identifier;
removeSource(sourceId: Identifier): void;
removeTarget(targetId: Identifier): void;
getSource(sourceId: Identifier, includePinned?: boolean): any;
getTarget(targetId: Identifier, includePinned?: boolean): any;
getSourceType(sourceId: Identifier): string | null;
getTargetType(targetId: Identifier): string | null;
isPinned(handlerId: Identifier): boolean;
pin(handlerId: Identifier): void;
unpin(handlerId: Identifier): void;
}
interface DragDropMonitor extends DragSourceMonitor, DropTargetMonitor, DragLayerMonitor {
// Additional methods specific to the combined monitor
}
// Connector functions
type ConnectDragSource = DragElementWrapper<DragSourceOptions>;
type ConnectDropTarget = DragElementWrapper<any>;
type ConnectDragPreview = DragElementWrapper<DragPreviewOptions>;
type ConnectableElement = React.RefObject<any> | React.ReactElement | Element | null;
// Utility types
type FactoryOrInstance<T> = T | (() => T);
type DragObjectFactory<T> = (monitor: DragSourceMonitor<T>) => T | null;
// Coordinate system
interface XYCoord {
x: number;
y: number;
}interface DragSourceMonitor<DragObject = unknown, DropResult = unknown> extends HandlerManager, MonitorEventEmitter {
canDrag(): boolean;
isDragging(): boolean;
getItemType(): Identifier | null;
getItem<T = DragObject>(): T;
getDropResult<T = DropResult>(): T | null;
didDrop(): boolean;
getInitialClientOffset(): XYCoord | null;
getInitialSourceClientOffset(): XYCoord | null;
getClientOffset(): XYCoord | null;
getDifferenceFromInitialOffset(): XYCoord | null;
getSourceClientOffset(): XYCoord | null;
getTargetIds(): Identifier[];
}
interface DropTargetMonitor<DragObject = unknown, DropResult = unknown> extends HandlerManager, MonitorEventEmitter {
canDrop(): boolean;
isOver(options?: { shallow?: boolean }): boolean;
getItemType(): Identifier | null;
getItem<T = DragObject>(): T;
getDropResult<T = DropResult>(): T | null;
didDrop(): boolean;
getInitialClientOffset(): XYCoord | null;
getInitialSourceClientOffset(): XYCoord | null;
getClientOffset(): XYCoord | null;
getDifferenceFromInitialOffset(): XYCoord | null;
getSourceClientOffset(): XYCoord | null;
}
interface DragLayerMonitor<DragObject = unknown> {
isDragging(): boolean;
getItemType(): Identifier | null;
getItem<T = DragObject>(): T;
getInitialClientOffset(): XYCoord | null;
getInitialSourceClientOffset(): XYCoord | null;
getClientOffset(): XYCoord | null;
getDifferenceFromInitialOffset(): XYCoord | null;
getSourceClientOffset(): XYCoord | null;
}interface DragSourceOptions {
dropEffect?: string;
}
interface DragPreviewOptions {
captureDraggingState?: boolean;
anchorX?: number;
anchorY?: number;
offsetX?: number;
offsetY?: number;
}
type DropTargetOptions = any;