Element adapter

Create, respond to and listen to element drag operations

The element adapter enables you to create rich drag and drop experiences, such as lists, boards, grids, resizing and so on.

The element adapter contains the essential pieces for element operations:

  • draggable: enable dragging of an element.
  • dropTargetForElements: marking an element as a valid drop target
  • monitorForElements: create a monitor to listen for element drag operation events anywhere.
  • types: all types for this adapter.

There are also a number of optional element utilities:

  • setCustomNativeDragPreview: use a new element as the native drag preview
  • pointerOutsideOfPreview: native drag preview function to place the users pointer outside of the drag preview
  • centerUnderPointer: native drag preview function to place the center of the ntaive drag preview under the users pointer
  • preserveOffsetOnSource: native drag preview function to match the pointer position on a native drag preview as close as possible to the pointer position on the draggable element
  • disableNativeDragPreview: disable the native drag preview (helpful if you want to use your own custom drag preview or have no drag preview)
  • scrollJustEnoughIntoView: scroll an element just enough into view so it is visible (helpful when working with default native drag previews)

It is likely that some top level utilities will be helpful for your experience as well

Draggable

A draggable is an HTMLElement that can be dragged around by a user.

A draggable can be located:

  • Outside of any drop targets
  • Inside any amount of levels of nested drop targets
  • So, anywhere!

While a drag operation is occurring:

  • You can add new draggables
  • You can remount a draggable. See Reconciliation
  • You can change the dimensions of the dragging draggable during a drag. But keep in mind that won't change the drag preview image, as that is collected only at the start of the drag (in onGenerateDragPreview())
  • You can remove the dragging draggable during a drag operation. When a draggable is removed it's event functions (eg onDrag) will no longer be called. Being able to remove the dragging draggable is a common requirement for virtual lists

Draggable argument overview

  • element: HTMLElement: a HTMLElement that will be draggable (using HTMLElement as that is the interface that allows the "draggable" attribute)
  • dragHandle?: Element: an optional Element that can be used to designate the part of the draggable that can exclusively used to drag the whole draggable
  • canDrag?: (args: GetFeedbackArgs) => boolean: used to conditionally allow dragging (see below)
  • getInitialData?: (args: GetFeedbackArgs) => Record<string, unknown>: a one time attaching of data to a draggable as a drag is starting. If you want to understand the type of data attached to a drop target elsewhere in your application, see our typing data guide.
  • getInitialDataForExternal?: (args: GetFeedbackArgs) => {[Key in NativeMediaType]?: string;}: used to attach native data (eg "text/plain") to other windows or applications.
type GetFeedbackArgs = {
    /**
     * The user input as a drag is trying to start (the `initial` input)
     */
    input: Input;
    /**
     * The `draggable` element
     */
    element: HTMLElement;
    /**
     * The `dragHandle` element for the `draggable`
     */
    dragHandle: Element | null;
};

Drag handles

A drag handle is the part of your draggable element that can be dragged in order to drag the whole draggable. By default, the entire draggable acts as a drag handle. However, you can optionally mark a child element of a draggable element as the drag handle.

draggable({
    element: myElement,
    dragHandle: myDragHandleElement,
});

You can also implement a drag handle by making a small part of an element the draggable, and then using setCustomNativeDragPreview to generate a preview for the entire entity.

Conditional dragging (canDrag())

A draggable can conditionally allow dragging by using the canDrag() function. Returning true from canDrag() will allow the drag, and returning false will prevent a drag.

draggable({
    element: myElement,
    // disable dragging
    canDrag: () => false,
});
Drop on me!Last dropped: none

Disabling a drag by returning false from canDrag() will prevent any other draggable on the page from being dragged. @atlaskit/pragmatic-drag-and-drop calls event.preventDefault() under the hood when canDrag() returns false, which cancels the drag operation. Unfortunately, once a drag event has started, a draggable element cannot individually opt out of dragging and allow another element to be dragged.

If you want to disable dragging for a draggable, but still want a parent draggable to be able to be dragged, then rather than using canDrag() you can conditionally apply draggable()

Here is example of what that could look like using react:

import { useEffect } from 'react';
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';

function noop(){};

function Item({isDraggingEnabled}: {isDraggingEnabled: boolean}) {
  const ref = useRef();

  useEffect({
    // when disabled, don't make the element draggable
    // this will allow a parent draggable to still be dragged
    if(!isDraggingEnabled) {
      return noop;
    }
    return draggable({
      element: ref.current,
    });
  }, [isDraggingEnabled]);

  return <div ref={ref}>Draggable item</div>
};

Data for external consumers (getInitialDataForExternal())

getInitialDataForExternal() allows you want to attach data to a drag operation that can be used by other windowss or applications (externally)

draggable({
    element: myElement,
    getInitialData: () => ({ taskId: task.id }),
    getInitialDataForExternal: () => ({
        'text/plain': task.description,
        'text/uri-list': task.url,
    }),
});

We also have a helper formatURLsForExternal(urls: string[]): string that allows you to attach multiple urls for external consumers.

import { formatURLsForExternal } from '@atlaskit/pragmatic-drag-and-drop/element/format-urls-for-external';

draggable({
    element: myElement,
    getInitialData: () => ({ taskId: task.id }),
    getInitialDataForExternal: () => ({
        'text/plain': task.description,
        'text/uri-list': formatURLsForExternal([task.url, task.anotherUrl]),
    }),
});

Data attached for external consumers can be accessed by any external consumer that the user drops on. It is important that you don't expose private data.

Attaching external data from a draggable will not trigger the external adapter in the window that the draggable started in, but it will trigger the external adapter in other windows (eg in <iframe>s).

Drop target for elements

A drop target for elements.

The default dropEffect for this type of drop target is "move". This lines up with our design guides. You can override this default with getDropEffect().

import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';

const cleanup = dropTargetForElements({
  element: myElement,
  onDragStart: () => console.log('Something started dragging in me!');
});

Monitor for elements

A monitor for elements.

import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';

const cleanup = monitorForElements({
  onDragStart: () => console.log('Dragging an element');
});

Types

Generally you won't need to explicitly use our provided types, but we expose a number of TypeScript types if you would like to use them.

All events on draggables, drop targets and monitors, are given the following base payload:

type ElementEventBasePayload = {
    location: DragLocationHistory;
    source: ElementDragPayload;
};

type ElementDragPayload = {
    element: HTMLElement;
    dragHandle: Element | null;
    data: Record<string, unknown>;
};

For all the arguments for all events, you can use our event map type:

type ElementEventPayloadMap = {
    onDragStart: ElementEventBasePayload;
    // .. the rest of the events
};

Draggable feedback functions (canDrag, getInitialData, getInitialDataForExternal) are given the following:

type ElementGetFeedbackArgs = {
    /**
     * The user input as a drag is trying to start (the `initial` input)
     */
    input: Input;
    /**
     * The `draggable` element
     */
    element: HTMLElement;
    /**
     * The `dragHandle` element for the `draggable`
     */
    dragHandle: Element | null;
};

Drop targets are given a little bit more information in each event:

type ElementDropTargetEventBasePayload = ElementEventBasePayload & {
    /**
     * A convenance pointer to this drop targets values
     */
    self: DropTargetRecord;
};

For all arguments for all events on drop targets, you can use our event map type:

type ElementDropTargetEventPayloadMap = {
    onDragStart: ElementDropTargetEventBasePayload;
    // .. the rest of the events
};

Drop target feedback functions (canDrop, getData, getDropEffect, getIsSticky) are given the following:

type ElementDropTargetGetFeedbackArgs = {
    /**
     * The users _current_ input
     */
    input: Input;
    /**
     * The data associated with the entity being dragged
     */
    source: ElementDragPayload;
    /**
     * This drop target's element
     */
    element: Element;
};

The monitor feedback function (canMonitor), is given the following:

type ElementMonitorGetFeedbackArgs = {
    /**
     * The users `initial` drag location
     */
    initial: DragLocation;
    /**
     * The data associated with the entity being dragged
     */
    source: ElementDragPayload;
};

You can get these type from the element adapter import:

import type {
    // Payload for the draggable being dragged
    ElementDragPayload,
    // Base events
    ElementEventBasePayload,
    ElementEventPayloadMap,
    // Drop target events
    ElementDropTargetEventBasePayload,
    ElementDropTargetEventPayloadMap,
    // Feedback types
    ElementGetFeedbackArgs,
    ElementDropTargetGetFeedbackArgs,
    ElementMonitorGetFeedbackArgs,
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';

There are also some types (eg DropTargetLocation) that can be used for all adapters which can be found on our top level utilities page

Further reading

Was this page helpful?
We use this feedback to improve our documentation.
© 2026 AtlassianTrademark, (opens new window)Privacy, (opens new window)License