The JavaScript Drag & Drop library your grandparents warned you about.
npx @tessl/cli install tessl/npm-shopify--draggable@1.1.0Shopify Draggable is a comprehensive JavaScript drag-and-drop library built with TypeScript that offers complete control over drag-and-drop behavior through a flexible API. It abstracts native browser events (drag, mouse, touch, and force touch) into a unified interface and includes specialized modules: Sortable for reordering elements, Droppable for defining drop zones, and Swappable for element swapping.
npm install @shopify/draggableimport { Draggable, Sortable, Droppable, Swappable } from "@shopify/draggable";Base classes and utilities:
import { BaseEvent, BasePlugin, Sensors, Plugins } from "@shopify/draggable";For CommonJS:
const { Draggable, Sortable, Droppable, Swappable } = require("@shopify/draggable");
const { BaseEvent, BasePlugin, Sensors, Plugins } = require("@shopify/draggable");ES modules (alternative imports):
import Draggable from "@shopify/draggable/lib/draggable";
import Sortable from "@shopify/draggable/lib/sortable";import { Draggable } from "@shopify/draggable";
// Basic draggable setup
const draggable = new Draggable(document.querySelectorAll('.container'), {
draggable: '.item'
});
// Listen to drag events
draggable.on('drag:start', (event) => {
console.log('Started dragging:', event.source);
});
draggable.on('drag:stop', (event) => {
console.log('Stopped dragging:', event.source);
});
// Cleanup when done
draggable.destroy();Shopify Draggable is built around several key components:
Basic drag-and-drop functionality with container management, event handling, and plugin system. Provides the foundation for all other draggable types.
class Draggable<TEventListType = DraggableEventNames> {
constructor(containers: DraggableContainer, options?: DraggableOptions);
destroy(): void;
on<T extends TEventListType>(eventName: T, callback: (event: GetEventByEventName<T>) => void): this;
off<T extends TEventListType>(eventName: T, callback: (event: GetEventByEventName<T>) => void): this;
trigger(event: AbstractEvent): void;
isDragging(): boolean;
}
type DraggableContainer = HTMLElement | HTMLElement[] | NodeList;Reorder elements within or between containers with automatic position tracking and smooth animations. Perfect for to-do lists, priority queues, and dashboard widgets.
class Sortable<T = SortableEventNames> extends Draggable<T> {
constructor(containers: DraggableContainer, options?: DraggableOptions);
index(element: HTMLElement): number;
getSortableElementsForContainer(container: HTMLElement): HTMLElement[];
}Create designated drop zones where draggable elements can be placed. Includes visual feedback and validation for valid/invalid drop targets.
class Droppable<T = DroppableEventNames> extends Draggable<T> {
constructor(containers: DraggableContainer, options: DroppableOptions);
getClassNameFor(name: DroppableClassNames): string;
}
interface DroppableOptions extends DraggableOptions {
dropzone: string | NodeList | HTMLElement[] | (() => NodeList | HTMLElement[]);
classes?: {[key in DroppableClassNames]: string};
}Enable element swapping where dragging over another element exchanges their positions. Order-independent interaction for grid layouts and card arrangements.
class Swappable<T = SwappableEventNames> extends Draggable<T> {
constructor(containers: DraggableContainer, options?: DraggableOptions);
}Comprehensive event lifecycle with drag events, specialized events for each draggable type, and plugin-specific events. All events extend AbstractEvent with cancellation support.
abstract class AbstractEvent<TData = {[key: string]: any}> {
readonly type: string;
readonly cancelable: boolean;
constructor(data: TData);
cancel(): void;
canceled(): boolean;
clone(data?: Partial<TData>): AbstractEvent<TData>;
}
export {AbstractEvent as BaseEvent};Input detection system supporting mouse, touch, native drag events, and force touch. Pluggable architecture allows custom sensor implementation for specialized input handling.
class Sensor {
constructor(containers: HTMLElement | HTMLElement[] | NodeList, options?: SensorOptions);
attach(): this;
detach(): this;
addContainer(...containers: HTMLElement[]): void;
removeContainer(...containers: HTMLElement[]): void;
trigger(element: HTMLElement, sensorEvent: SensorEvent): SensorEvent;
}Extensible plugin system with built-in plugins for mirror elements, auto-scrolling, accessibility announcements, focus management, collision detection, and animations.
abstract class AbstractPlugin {
constructor(draggable: Draggable);
protected abstract attach(): void;
protected abstract detach(): void;
}
export {AbstractPlugin as BasePlugin};interface DraggableOptions {
draggable?: string;
distance?: number;
handle?: string | NodeList | HTMLElement[] | HTMLElement | ((currentElement: HTMLElement) => HTMLElement);
delay?: number | DelayOptions;
placedTimeout?: number;
plugins?: (typeof AbstractPlugin)[];
sensors?: Sensor[];
exclude?: {
plugins?: (typeof AbstractPlugin)[];
sensors?: (typeof Sensor)[];
};
classes?: {[key in DraggableClassNames]: string | string[]};
announcements?: AnnouncementOptions;
collidables?: Collidables;
mirror?: MirrorOptions;
scrollable?: ScrollableOptions;
swapAnimation?: SwapAnimationOptions;
sortAnimation?: SortAnimationOptions;
}
interface DelayOptions {
mouse?: number;
drag?: number;
touch?: number;
}
type DraggableClassNames =
| 'body:dragging'
| 'container:dragging'
| 'source:dragging'
| 'source:placed'
| 'container:placed'
| 'draggable:over'
| 'container:over'
| 'source:original'
| 'mirror';