React component for creating draggable and resizable UI elements with comprehensive interaction controls
—
Callback props for all drag and resize interaction phases with detailed event data. React RnD provides comprehensive event handling for all user interactions.
Handle all phases of drag interactions with detailed position and delta information.
/**
* Called when drag operation starts
* Can return false to cancel the drag operation
*/
onDragStart?: RndDragCallback;
/**
* Called continuously during drag operation
* Can return false to cancel the drag operation
*/
onDrag?: RndDragCallback;
/**
* Called when drag operation completes
*/
onDragStop?: RndDragCallback;
// RndDragCallback is imported from react-draggable as DraggableEventHandler
type RndDragCallback = import("react-draggable").DraggableEventHandler;
type RndDragEvent =
| React.MouseEvent<HTMLElement | SVGElement>
| React.TouchEvent<HTMLElement | SVGElement>
| MouseEvent
| TouchEvent;
interface DraggableData extends Position {
/** DOM element being dragged */
node: HTMLElement;
/** X coordinate in pixels */
x: number;
/** Y coordinate in pixels */
y: number;
/** Change in X since last event */
deltaX: number;
/** Change in Y since last event */
deltaY: number;
/** Previous X coordinate */
lastX: number;
/** Previous Y coordinate */
lastY: number;
}Usage Examples:
function DragTrackingExample() {
const [dragInfo, setDragInfo] = React.useState<string>('');
return (
<div>
<div>Drag Status: {dragInfo}</div>
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onDragStart={(e, d) => {
setDragInfo(`Drag started at (${d.x}, ${d.y})`);
console.log('Drag started:', d);
}}
onDrag={(e, d) => {
setDragInfo(`Dragging to (${d.x}, ${d.y}), delta: (${d.deltaX}, ${d.deltaY})`);
}}
onDragStop={(e, d) => {
setDragInfo(`Drag ended at (${d.x}, ${d.y})`);
console.log('Final position:', d.x, d.y);
}}
>
Drag me and watch the console
</Rnd>
</div>
);
}
// Conditional drag prevention
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onDragStart={(e, d) => {
// Prevent drag if certain condition is met
if (someCondition) {
console.log('Drag prevented');
return false; // Cancel drag
}
}}
>
Conditionally draggable
</Rnd>Handle all phases of resize interactions with size delta and position information.
/**
* Called when resize operation starts
* Can return false to cancel the resize operation
*/
onResizeStart?: RndResizeStartCallback;
/**
* Called continuously during resize operation
*/
onResize?: RndResizeCallback;
/**
* Called when resize operation completes
*/
onResizeStop?: RndResizeCallback;
type RndResizeStartCallback = (
e: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>,
dir: ResizeDirection,
elementRef: HTMLElement
) => void | boolean;
type RndResizeCallback = (
e: MouseEvent | TouchEvent,
dir: ResizeDirection,
elementRef: HTMLElement,
delta: ResizableDelta,
position: Position
) => void;
interface ResizableDelta {
/** Change in width since resize started */
width: number;
/** Change in height since resize started */
height: number;
}
type ResizeDirection =
| "top" | "right" | "bottom" | "left"
| "topRight" | "bottomRight" | "bottomLeft" | "topLeft";Usage Examples:
function ResizeTrackingExample() {
const [resizeInfo, setResizeInfo] = React.useState<string>('');
return (
<div>
<div>Resize Status: {resizeInfo}</div>
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onResizeStart={(e, dir, ref) => {
setResizeInfo(`Resize started from ${dir} handle`);
console.log('Resize started:', dir, ref.getBoundingClientRect());
}}
onResize={(e, dir, ref, delta, position) => {
setResizeInfo(
`Resizing ${dir}: ${ref.style.width} x ${ref.style.height}, ` +
`delta: (${delta.width}, ${delta.height}), pos: (${position.x}, ${position.y})`
);
}}
onResizeStop={(e, dir, ref, delta, position) => {
setResizeInfo(`Resize ended: ${ref.style.width} x ${ref.style.height}`);
console.log('Final size:', ref.style.width, ref.style.height);
console.log('Final position:', position);
}}
>
Resize me and watch the console
</Rnd>
</div>
);
}
// Conditional resize prevention
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onResizeStart={(e, dir, ref) => {
// Prevent resize from certain handles
if (dir === 'top' || dir === 'topLeft' || dir === 'topRight') {
console.log('Top resize prevented');
return false; // Cancel resize
}
}}
>
Only bottom/side resize allowed
</Rnd>Integrate with React state management for controlled components.
function ControlledRndExample() {
const [state, setState] = React.useState({
x: 0,
y: 0,
width: 200,
height: 150,
});
return (
<Rnd
size={{ width: state.width, height: state.height }}
position={{ x: state.x, y: state.y }}
onDragStop={(e, d) => {
setState(prev => ({
...prev,
x: d.x,
y: d.y,
}));
}}
onResizeStop={(e, direction, ref, delta, position) => {
setState({
width: ref.style.width,
height: ref.style.height,
...position,
});
}}
>
Controlled component with state
</Rnd>
);
}Handle general mouse events on the component.
/**
* Called on mouse down events
* Note: This is a native MouseEvent, not React SyntheticEvent
*/
onMouseDown?: (e: MouseEvent) => void;
/**
* Called on mouse up events
* Note: This is a native MouseEvent, not React SyntheticEvent
*/
onMouseUp?: (e: MouseEvent) => void;Usage Examples:
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onMouseDown={(e) => {
console.log('Mouse down at:', e.clientX, e.clientY);
}}
onMouseUp={(e) => {
console.log('Mouse up at:', e.clientX, e.clientY);
}}
>
General mouse event handling
</Rnd>Coordinate between drag and resize events for complex interactions.
function CoordinatedEventsExample() {
const [isDragging, setIsDragging] = React.useState(false);
const [isResizing, setIsResizing] = React.useState(false);
return (
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onDragStart={() => {
setIsDragging(true);
console.log('Started dragging');
}}
onDragStop={() => {
setIsDragging(false);
console.log('Stopped dragging');
}}
onResizeStart={() => {
setIsResizing(true);
console.log('Started resizing');
}}
onResizeStop={() => {
setIsResizing(false);
console.log('Stopped resizing');
}}
style={{
border: isDragging ? '2px solid blue' : isResizing ? '2px solid red' : '1px solid gray',
}}
>
Visual feedback during interactions
</Rnd>
);
}Use event callbacks to implement custom validation and constraints.
function ValidationExample() {
const [errors, setErrors] = React.useState<string[]>([]);
return (
<div>
{errors.map((error, i) => (
<div key={i} style={{ color: 'red' }}>{error}</div>
))}
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onDrag={(e, d) => {
const newErrors: string[] = [];
if (d.x < 0) newErrors.push('Cannot move to negative X');
if (d.y < 0) newErrors.push('Cannot move to negative Y');
setErrors(newErrors);
}}
onResize={(e, dir, ref, delta, position) => {
const newErrors: string[] = [];
const width = parseInt(ref.style.width);
const height = parseInt(ref.style.height);
if (width > 500) newErrors.push('Width cannot exceed 500px');
if (height > 400) newErrors.push('Height cannot exceed 400px');
setErrors(newErrors);
}}
>
Validation feedback during interaction
</Rnd>
</div>
);
}Optimize event handling for performance-critical applications.
function OptimizedEventsExample() {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
const [size, setSize] = React.useState({ width: 200, height: 150 });
// Debounced state updates
const debouncedUpdatePosition = React.useMemo(
() => debounce((x: number, y: number) => setPosition({ x, y }), 100),
[]
);
return (
<Rnd
default={{ x: 0, y: 0, width: 200, height: 150 }}
onDrag={(e, d) => {
// Update immediately for smooth interaction
// Debounced state update for expensive operations
debouncedUpdatePosition(d.x, d.y);
}}
onResizeStop={(e, dir, ref, delta, pos) => {
// Only update state on stop for better performance
setSize({
width: parseInt(ref.style.width),
height: parseInt(ref.style.height)
});
setPosition(pos);
}}
>
Performance optimized events
</Rnd>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-rnd