Skip to content

useDraggable

Category
Export Size
1.90 kB
Last Changed
5 days ago

Make elements draggable.

Demo

Usage

tsx
import { 
useDraggable
} from '@reause/core'
import {
useRef
} from 'react'
const
el
=
useRef
<HTMLDivElement>(null)
// `style` will be a helper string for `left: ?px; top: ?px;` const {
x
,
y
,
style
} =
useDraggable
(
el
, {
initialValue
: {
x
: 40,
y
: 40 },
})
tsx
// `style` is a helper string for `left: ?px; top: ?px;` — convert it into a
// React style object for the `style` prop (or use `x` / `y` directly)
function 
styleStringToObject
(
style
: string):
Record
<string, string> {
const
result
:
Record
<string, string> = {}
for (const
declaration
of
style
.
split
(';')) {
const [
key
,
value
] =
declaration
.
split
(':').
map
(
part
=>
part
.
trim
())
if (
key
&&
value
)
result
[
key
] =
value
} return
result
} <
div
ref={el} style={{ position: 'fixed', ...
styleStringToObject
(style) }}>
Drag me! I am at {' '} {x} , {' '} {y} </div>

Return Values

StateTypeDescription
xnumberCurrent x position
ynumberCurrent y position
positionPositionCurrent position object
isDraggingbooleanWhether currently dragging
stylestringCSS style string left: ?px; top: ?px;

Options

ts
useDraggable
(el, {
// Initial position (default: { x: 0, y: 0 })
initialValue
: {
x
: 40,
y
: 40 },
// Restrict dragging to specific axis: 'x', 'y', or 'both' (default)
axis
: 'both',
// Only trigger when clicking directly on the element (default: false)
exact
: false,
// Prevent default browser behavior (default: false)
preventDefault
: true,
// Stop event propagation (default: false)
stopPropagation
: false,
// Use capture phase for events (default: true)
capture
: true,
// Disable dragging (default: false)
disabled
: false,
// Mouse buttons that trigger drag (default: [0] - left button)
buttons
: [0],
// Pointer types to listen to (default: ['mouse', 'touch', 'pen'])
pointerTypes
: ['mouse', 'touch', 'pen'],
// Custom drag handle element (default: target element)
handle
: handleRef,
// Container element for bounds (default: none)
containerElement
: containerRef,
// Element to attach pointermove/pointerup events (default: window)
draggingElement
:
window
,
// Callbacks
onStart
: (
position
,
event
) => {
// Return false to prevent dragging },
onMove
: (
position
,
event
) => {},
onEnd
: (
position
,
event
) => {},
})

Prevent Default

Set preventDefault: true to override the default drag-and-drop behavior of certain elements in the browser (e.g., images).

ts
import { 
useDraggable
} from '@reause/core'
const {
x
,
y
,
style
} =
useDraggable
(el, {
preventDefault
: true,
})

Container Bounds

Use containerElement to constrain dragging within a container.

ts
const { 
x
,
y
} =
useDraggable
(el, {
containerElement
: containerRef,
})

Set autoScroll: true to enable auto-scroll when dragging near the edges.

ts
const { 
x
,
y
,
style
} =
useDraggable
(el, {
autoScroll
: {
speed
: 2, // Control the speed of auto-scroll.
margin
: 30, // Set the margin from the edge that triggers auto-scroll.
direction
: 'both', // Determine the direction of auto-scroll.
}, })

Type Declarations

Toggle
ts
export interface Position {
    x: number;
    y: number;
}

export type DraggableTarget = RefObject<HTMLElement | SVGElement | null | undefined>

export type DraggableElement = RefObject<HTMLElement | SVGElement | Window | Document | null | undefined>

export type DraggableContainer = RefObject<HTMLElement | SVGElement | null | undefined>

export interface UseDraggableOptions {
    exact?: boolean;
    preventDefault?: boolean;
    stopPropagation?: boolean;
    capture?: boolean;
    draggingElement?: DraggableElement;
    containerElement?: DraggableContainer;
    handle?: DraggableTarget;
    pointerTypes?: PointerType[];
    initialValue?: Position;
    onStart?: (position: Position, event: PointerEvent) => void | false;
    onMove?: (position: Position, event: PointerEvent) => void;
    onEnd?: (position: Position, event: PointerEvent) => void;
    axis?: 'x' | 'y' | 'both';
    disabled?: boolean;
    buttons?: number[];
    restrictInView?: boolean;
    autoScroll?: boolean | {
        speed?: number | Position;
        margin?: number | Position;
        direction?: 'x' | 'y' | 'both';
    };
}

export interface UseDraggableReturn {
    x: number;
    y: number;
    position: Position;
    isDragging: boolean;
    style: string;
    setX: (value: number) => void;
    setY: (value: number) => void;
}

export type PointerType = 'mouse' | 'touch' | 'pen'

export interface UsePointerState {
    x: number;
    y: number;
    pointerId: number;
    pressure: number;
    tiltX: number;
    tiltY: number;
    width: number;
    height: number;
    twist: number;
    pointerType: PointerType | null;
}

export interface UsePointerOptions extends ConfigurableWindow {
    pointerTypes?: PointerType[];
    initialValue?: Partial<UsePointerState>;
    target?: RefObject<EventTarget | null | undefined>;
}

export function useDraggable(target: DraggableTarget, options?: UseDraggableOptions): UseDraggableReturn

Source

Source · Demo · VueUse

Contributors

hairyf

Changelog

v0.1.0 on
e608a - feat(ci): align CI, packaging and release with VueUse
aa84f - fix(core): resolve useDraggable audit findings (#545)
8fddc - chore!: remove all Vue-only Maybe* types and getter unions, adopt React Ref (#462)

Released under the MIT License. v0.1.8