React components for adding drag-and-drop functionality to React applications with position management and boundary controls
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
DraggableCore is a low-level, stateless React component that provides drag events without any position management or visual transforms. It gives complete control over drag behavior to the parent component, making it perfect for custom drag implementations, complex interactions, or when you need full control over positioning logic.
The stateless draggable component that only provides drag events.
/**
* A low-level draggable component with no internal state
* Provides drag events but no position management
* @param props - DraggableCore configuration properties
*/
function DraggableCore(props: DraggableCoreProps): React.ReactElement;
interface DraggableCoreProps {
// Interaction controls
handle?: string;
cancel?: string;
disabled?: boolean;
allowAnyClick?: boolean;
allowMobileScroll?: boolean;
// Event handlers (required for functionality)
onStart?: DraggableEventHandler;
onDrag?: DraggableEventHandler;
onStop?: DraggableEventHandler;
onMouseDown?: (e: MouseEvent) => void;
// Advanced options
enableUserSelectHack?: boolean;
offsetParent?: HTMLElement;
grid?: [number, number];
scale?: number;
nodeRef?: React.RefObject<HTMLElement>;
// Required
children: React.ReactNode;
}Usage Examples:
import React, { useState } from 'react';
import { DraggableCore } from 'react-draggable';
// Basic DraggableCore with manual positioning
function CustomDraggable() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [dragging, setDragging] = useState(false);
const handleStart = (e, data) => {
setDragging(true);
};
const handleDrag = (e, data) => {
setPosition({
x: position.x + data.deltaX,
y: position.y + data.deltaY
});
};
const handleStop = (e, data) => {
setDragging(false);
};
return (
<DraggableCore
onStart={handleStart}
onDrag={handleDrag}
onStop={handleStop}
>
<div
style={{
transform: `translate(${position.x}px, ${position.y}px)`,
backgroundColor: dragging ? '#ff0000' : '#0000ff',
padding: '10px',
cursor: dragging ? 'grabbing' : 'grab'
}}
>
Custom draggable with manual positioning
</div>
</DraggableCore>
);
}DraggableCore relies entirely on event handlers for functionality since it maintains no internal state.
/**
* Called when drag starts
* Must be provided to handle drag initiation
* Return false to cancel the drag
*/
onStart?: DraggableEventHandler;
/**
* Called continuously during drag movement
* Must be provided to handle position updates
* Return false to stop the current drag
*/
onDrag?: DraggableEventHandler;
/**
* Called when drag ends
* Handle final positioning and cleanup
*/
onStop?: DraggableEventHandler;
/**
* Called on mouse down events before drag consideration
*/
onMouseDown?: (e: MouseEvent) => void;
type DraggableEventHandler = (
e: DraggableEvent,
data: DraggableData
) => void | false;
interface DraggableData {
node: HTMLElement; // The dragged DOM element
x: number; // Absolute x position relative to offset parent
y: number; // Absolute y position relative to offset parent
deltaX: number; // Change in x since last drag event
deltaY: number; // Change in y since last drag event
lastX: number; // Previous absolute x position
lastY: number; // Previous absolute y position
}Usage Examples:
import React, { useState, useRef } from 'react';
import { DraggableCore } from 'react-draggable';
// Complex drag behavior with momentum
function MomentumDraggable() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [velocity, setVelocity] = useState({ x: 0, y: 0 });
const lastTime = useRef(Date.now());
const handleDrag = (e, data) => {
const now = Date.now();
const dt = (now - lastTime.current) / 1000;
// Calculate velocity
const vx = data.deltaX / dt;
const vy = data.deltaY / dt;
setVelocity({ x: vx, y: vy });
// Update position
setPosition({
x: position.x + data.deltaX,
y: position.y + data.deltaY
});
lastTime.current = now;
};
const handleStop = (e, data) => {
// Apply momentum after drag stops
const momentumX = velocity.x * 0.1;
const momentumY = velocity.y * 0.1;
setPosition({
x: position.x + momentumX,
y: position.y + momentumY
});
};
return (
<DraggableCore onDrag={handleDrag} onStop={handleStop}>
<div
style={{
transform: `translate(${position.x}px, ${position.y}px)`,
transition: velocity.x !== 0 ? 'transform 0.3s ease-out' : 'none'
}}
>
Momentum draggable
</div>
</DraggableCore>
);
}Configure drag handles, cancel zones, and interaction behavior (same as Draggable).
/**
* CSS selector for elements that can initiate drag
*/
handle?: string;
/**
* CSS selector for elements that prevent drag initiation
*/
cancel?: string;
/**
* Completely disable drag functionality
*/
disabled?: boolean;
/**
* Allow dragging with any mouse button
*/
allowAnyClick?: boolean;
/**
* Allow normal mobile scrolling behavior
*/
allowMobileScroll?: boolean;Usage Examples:
// DraggableCore with handle and cancel
function HandleDraggableCore() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleDrag = (e, data) => {
setPosition({
x: position.x + data.deltaX,
y: position.y + data.deltaY
});
};
return (
<DraggableCore
handle=".drag-handle"
cancel=".no-drag"
onDrag={handleDrag}
>
<div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
<div className="drag-handle" style={{ cursor: 'grab' }}>
✋ Drag Handle
</div>
<div>
<button className="no-drag">Button (not draggable)</button>
<p>Content area</p>
</div>
</div>
</DraggableCore>
);
}Apply grid snapping and scaling to drag calculations.
/**
* Snap drag movements to grid positions
* [x, y] - grid cell size in pixels
*/
grid?: [number, number];
/**
* Scale factor for drag distance calculations
* Useful for zoomed/scaled containers
*/
scale?: number;Usage Examples:
// Grid-snapped DraggableCore
function GridDraggableCore() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleDrag = (e, data) => {
// Note: grid snapping is applied to the data values automatically
setPosition({
x: data.x, // Already snapped to grid
y: data.y // Already snapped to grid
});
};
return (
<DraggableCore grid={[25, 25]} onDrag={handleDrag}>
<div
style={{
transform: `translate(${position.x}px, ${position.y}px)`,
width: '50px',
height: '50px',
backgroundColor: '#4CAF50'
}}
>
Grid Snap
</div>
</DraggableCore>
);
}Fine-tune behavior for specific use cases and React compatibility.
/**
* Add user-select: none during drag to prevent text selection
* Default: true
*/
enableUserSelectHack?: boolean;
/**
* Custom offset parent for position calculations
*/
offsetParent?: HTMLElement;
/**
* React ref for the draggable element
* Recommended for React Strict Mode compatibility
*/
nodeRef?: React.RefObject<HTMLElement>;Usage Examples:
import React, { useRef, useState } from 'react';
import { DraggableCore } from 'react-draggable';
// React Strict Mode compatible DraggableCore
function StrictModeDraggableCore() {
const nodeRef = useRef(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleDrag = (e, data) => {
setPosition({
x: position.x + data.deltaX,
y: position.y + data.deltaY
});
};
return (
<DraggableCore nodeRef={nodeRef} onDrag={handleDrag}>
<div
ref={nodeRef}
style={{
transform: `translate(${position.x}px, ${position.y}px)`
}}
>
Strict Mode Compatible
</div>
</DraggableCore>
);
}DraggableCore is ideal for scenarios requiring custom drag behavior:
Custom Positioning Logic:
// Custom boundary checking
function BoundedDraggableCore() {
const [position, setPosition] = useState({ x: 100, y: 100 });
const handleDrag = (e, data) => {
// Custom boundary logic
const newX = Math.max(0, Math.min(400, position.x + data.deltaX));
const newY = Math.max(0, Math.min(300, position.y + data.deltaY));
setPosition({ x: newX, y: newY });
};
return (
<DraggableCore onDrag={handleDrag}>
<div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
Custom bounded
</div>
</DraggableCore>
);
}Multi-element Coordination:
// Dragging multiple elements simultaneously
function MultiElementDrag() {
const [positions, setPositions] = useState([
{ x: 0, y: 0 }, { x: 100, y: 0 }, { x: 200, y: 0 }
]);
const handleDrag = (index) => (e, data) => {
setPositions(prev => prev.map((pos, i) =>
i === index
? { x: pos.x + data.deltaX, y: pos.y + data.deltaY }
: { x: pos.x + data.deltaX * 0.5, y: pos.y + data.deltaY * 0.5 } // Follow drag
));
};
return (
<div>
{positions.map((pos, index) => (
<DraggableCore key={index} onDrag={handleDrag(index)}>
<div
style={{
transform: `translate(${pos.x}px, ${pos.y}px)`,
width: '50px',
height: '50px',
backgroundColor: index === 0 ? '#ff0000' : '#cccccc'
}}
>
{index === 0 ? 'Leader' : 'Follower'}
</div>
</DraggableCore>
))}
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-draggable