Skip to content

useDnR

The useDnR hook combines both drag and resize functionality into a single composable hook, allowing you to create elements that can be both moved and resized.

Demo

👋 Drag & ↔️ Resize me!
Position: 0, 0
Size: 200 x 150
Hook Usage
vue
<script setup>
import { ref } from 'vue'
import { useDnR } from 'vue-dndnr'

const elementRef = ref(null)
const { position, size, style } = useDnR(elementRef, {
  initialPosition: { x: 50, y: 50 },
  initialSize: { width: 200, height: 150 },
  minWidth: 100,
  minHeight: 100
})
</script>

<template>
  <div ref="elementRef" :style="style">
    Drag & Resize me!
    <div>Position: {{ position.x }}, {{ position.y }}</div>
    <div>Size: {{ size.width }} x {{ size.height }}</div>
  </div>
</template>
Component Usage

The DnR component provides a convenient wrapper around the useDnR hook, making it easier to create draggable and resizable elements without manually setting up refs and styles.

vue
<script setup>
import { ref } from 'vue'
import { DnR } from 'vue-dndnr'

const position = ref({ x: 50, y: 50 })
const size = ref({ width: 200, height: 150 })
</script>

<template>
  <DnR v-model:position="position" v-model:size="size" :min-width="100" :min-height="100">
    <div :style="{ width: `${size.width}px`, height: `${size.height}px` }">
      Drag and resize me!
      <div>Position: {{ position.x }}, {{ position.y }}</div>
      <div>Size: {{ size.width }} x {{ size.height }}</div>
    </div>
  </DnR>
</template>

Type and Options Declarations

Parameters

ParameterTypeDescription
targetMaybeRefOrGetter<HTMLElement | SVGElement | null | undefined>Reference to the element to make draggable and resizable
optionsDnROptionsConfiguration options for the draggable and resizable behavior

Return Values

PropertyTypeDescription
positionRef<Position>Current position of the element
sizeRef<Size>Current size of the element
styleComputedRef<CSSProperties>Computed CSS style object to apply to the element
isDraggingRef<boolean>Whether the element is currently being dragged
isResizingRef<boolean>Whether the element is currently being resized
isActiveRef<boolean>Whether the element is currently active
interactionModeRef<'idle' | 'dragging' | 'resizing'>Current interaction mode
activeHandleRef<ResizeHandle | null>Currently active resize handle
hoverHandleRef<ResizeHandle | null>Resize handle currently being hovered
setPosition(position: Position) => voidFunction to programmatically set the position
setSize(size: Size) => voidFunction to programmatically set the size
setActive(active: boolean) => voidFunction to programmatically set the active state
registerHandle(handle: ResizeHandle, element: HTMLElement) => voidRegister a custom resize handle
unregisterHandle(handle: ResizeHandle) => voidUnregister a custom resize handle
detectBoundary(event: PointerEvent, el: HTMLElement) => ResizeHandle | nullDetects if a pointer event is near a resizable boundary
setupHandleElements(parentElement: HTMLElement) => voidSets up or recreates resize handle elements

Options

The DnROptions interface provides a comprehensive set of configuration options:

Common Options

OptionTypeDefaultDescription
initialPositionPosition{ x: 0, y: 0 }Initial position of the element
initialSizeSize{ width: 'auto', height: 'auto' }Initial size of the element
disabledMaybeRefOrGetter<boolean>falseWhether all interactions are disabled
disableDragMaybeRefOrGetter<boolean>falseWhether dragging is disabled
disableResizeMaybeRefOrGetter<boolean>falseWhether resizing is disabled
pointerTypesMaybeRefOrGetter<PointerType[]>['mouse', 'touch', 'pen']Types of pointer events to respond to
preventDefaultMaybeRefOrGetter<boolean>trueWhether to prevent default browser events
stopPropagationMaybeRefOrGetter<boolean>falseWhether to stop event propagation
captureMaybeRefOrGetter<boolean>trueWhether to use event capturing phase
initialActivebooleanfalseInitial active state
activeOnMaybeRefOrGetter<ActivationTrigger>'none'How the element becomes active
preventDeactivationMaybeRefOrGetter<boolean>falsePrevent deactivation when clicking outside
throttleDelaynumber16Delay in ms for throttling move events
stateStylesMaybeRefOrGetter<Partial<StateStyles>>{}Custom styles for different element states

Drag Options

OptionTypeDefaultDescription
handleMaybeRefOrGetter<HTMLElement | SVGElement | null | undefined>targetElement that triggers dragging
containerElementMaybeRefOrGetter<HTMLElement | SVGElement | null | undefined>undefinedElement for calculating bounds and limiting draggable element boundaries
gridMaybeRefOrGetter<[number, number] | undefined | null>undefinedGrid size for snapping
axisMaybeRefOrGetter<'x' | 'y' | 'both'>'both'Axis to constrain movement
scaleMaybeRefOrGetter<number>1Scale factor for the element

Resize Options

OptionTypeDefaultDescription
positionTypeMaybeRefOrGetter<'absolute' | 'relative'>'absolute'CSS position type
minWidthMaybeRefOrGetter<number>undefinedMinimum width constraint
minHeightMaybeRefOrGetter<number>undefinedMinimum height constraint
maxWidthMaybeRefOrGetter<number>undefinedMaximum width constraint
maxHeightMaybeRefOrGetter<number>undefinedMaximum height constraint
lockAspectRatioMaybeRefOrGetter<boolean>falseMaintain aspect ratio during resize
handleTypeMaybeRefOrGetter<ResizeHandleType>'borders'Type of resize handles: 'borders', 'handles', 'custom', or 'none'
handlesMaybeRefOrGetter<ResizeHandle[]>['t', 'b', 'r', 'l', 'tr', 'tl', 'br', 'bl']Active resize handles
customHandlesMaybeRefOrGetter<Map<ResizeHandle, HTMLElement> | null | undefined>undefinedCustom handle elements
handlesSizeMaybeRefOrGetter<number>8Size of handles in pixels
handleStylesMaybeRefOrGetter<Partial<HandleStyles>>{}Custom styles for resize handles in different states
zIndexMaybeRefOrGetter<string | number>'auto'Z-index value for the element

Callback Options

OptionTypeDescription
onDragStart(position: Position, event: PointerEvent) => voidCalled when dragging starts
onDrag(position: Position, event: PointerEvent) => voidCalled during dragging
onDragEnd(position: Position, event: PointerEvent) => voidCalled when dragging ends
onResizeStart(size: Size, event: PointerEvent, handle: ResizeHandle) => voidCalled when resizing starts
onResize(size: Size, event: PointerEvent, handle: ResizeHandle) => voidCalled during resizing
onResizeEnd(size: Size, event: PointerEvent, handle: ResizeHandle) => voidCalled when resizing ends
onActiveChange(active: boolean) => booleanCalled when active state changes

Component Props

The DnR component accepts all options from the useDnR hook as props, plus the following:

PropTypeDefaultDescription
positionPositionundefinedCurrent position of the element. Can be bound with v-model:position.
sizeSizeundefinedCurrent size of the element. Can be bound with v-model:size.
activebooleanundefinedWhether the element is currently active. Can be bound with v-model:active.
classNamestringundefinedCSS class to apply to the wrapper element.
draggingClassNamestringundefinedCSS class to apply when dragging.
resizingClassNamestringundefinedCSS class to apply when resizing.
activeClassNamestringundefinedCSS class to apply when active.

Component Events

EventParametersDescription
update:positionPositionEmitted when position changes. Used for v-model:position binding.
update:sizeSizeEmitted when size changes. Used for v-model:size binding.
update:activebooleanEmitted when active state changes. Used for v-model:active binding.
activeChangebooleanEmitted when active state changes.
dragStartposition: Position, event: PointerEventEmitted when dragging starts.
dragposition: Position, event: PointerEventEmitted during dragging.
dragEndposition: Position, event: PointerEventEmitted when dragging ends.
resizeStartsize: Size, event: PointerEvent, handle: ResizeHandleEmitted when resizing starts.
resizesize: Size, event: PointerEvent, handle: ResizeHandleEmitted during resizing.
resizeEndsize: Size, event: PointerEvent, handle: ResizeHandleEmitted when resizing ends.

Component Slots

SlotPropsDescription
default{ position, size, isDragging, isResizing, isActive, activeHandle, hoverHandle, style }The content to be made draggable and resizable.
handle-[position]{ active, hover, isResizing, cursor, size }Custom resize handle for the specified position (e.g., handle-br for bottom-right). Only used when handleType="custom".

Type Definitions

StateStyles

The StateStyles interface allows you to customize the appearance of the element in different states:

typescript
interface StateStyles {
  /**
   * Styles applied when the element is active
   */
  active?: Record<string, string>

  /**
   * Styles applied when the element is being dragged
   */
  dragging?: Record<string, string>

  /**
   * Styles applied when the element is being resized
   */
  resizing?: Record<string, string>
}

HandleStyles

The HandleStyles interface allows you to customize the appearance of resize handles in different states:

typescript
interface HandleStyles {
  /**
   * Styles for handles in default state
   * Supports all CSS style properties
   */
  default?: Record<string, string>

  /**
   * Styles for handles in hover state
   * Supports all CSS style properties
   */
  hover?: Record<string, string>

  /**
   * Styles for handles in active state
   * Supports all CSS style properties
   */
  active?: Record<string, string>
}

ResizeHandleType

The ResizeHandleType type defines the available types of resize handles:

typescript
type ResizeHandleType = 'borders' | 'handles' | 'custom' | 'none'
  • 'borders': Uses the element's borders as resize handles (default)
  • 'handles': Displays visible handles at corners and edges
  • 'custom': Uses custom handles provided via slots or the customHandles option
  • 'none': Disables resize handles completely

Released under the MIT License.