> ## Documentation Index
> Fetch the complete documentation index at: https://dndkit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Draggable

> Make elements draggable to drop them over droppable targets.

export const draggableStyles = `
body {
  padding: 1em;
  font-family: system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.btn {
  display: flex;
  width: min-content;
  height: min-content;
  align-items: center;
  gap: 6px;
  font-size: 20px;
  font-weight: 900;
  cursor: grab;
  padding: 14px 18px;
  background: #000;
  color: #FFF;
  border-radius: 10px;
  border: none;
  transition: transform 0.25s ease, box-shadow 0.2s ease;
}

.btn::before {
  content: '';
  display: inline-block;
  width: 34px;
  height: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='43' height='43' viewBox='0 0 43 43' fill='none'%3E%3Cpath d='M4.55229 25.6017L20.5499 20.8985C21.4951 20.6173 22.3778 21.5001 22.1044 22.4532L17.402 38.4532C17.0817 39.5392 15.5897 39.672 15.082 38.6563L12.8401 34.1798C12.7855 34.0782 12.723 33.9767 12.6449 33.8907L5.77086 40.7657C4.79444 41.7423 3.20873 41.7423 2.23231 40.7657C1.2559 39.7892 1.2559 38.2032 2.23231 37.2267L9.10631 30.3517C9.02038 30.2735 8.92665 30.2032 8.81729 30.1563L4.34919 27.922C3.33372 27.4142 3.46651 25.922 4.55229 25.6017Z' fill='white'/%3E%3Cpath transform-origin='25 50%25' d='M22.4324 30.2031L21.3153 34H21.5028C28.408 34 34.001 28.4062 34.001 21.5C34.001 14.5938 28.408 9 21.5028 9C14.5976 9 9.00464 14.5938 9.00464 21.5V21.6875L12.801 20.5703C13.2696 16.1719 16.9878 12.75 21.5028 12.75C26.338 12.75 30.2515 16.6641 30.2515 21.5C30.2515 26.0156 26.8302 29.7344 22.4324 30.2031Z' fill='white' fill-opacity='0.7'%3E%3Canimate attributeName='fill-opacity' begin='0' values='0;0.7;0.7;0' keyTimes='0; 0.2; 0.85; 1' dur='4s' repeatCount='indefinite'/%3E%3CanimateTransform attributeName='transform' type='scale' values='0 0; 1 1; 1 1; 0 0' keyTimes='0; 0.1; 0.9; 1' begin='0s' dur='4s' repeatCount='indefinite'/%3E%3C/path%3E%3Cpath transform-origin='25 50%25' d='M37.7505 21.5C37.7505 30.4766 30.4782 37.75 21.5029 37.75V37.7422C21.0733 37.7422 20.6515 37.7266 20.2297 37.6953L19.1517 41.3594C19.925 41.4531 20.7062 41.5 21.5029 41.5C32.5482 41.5 41.5 32.5469 41.5 21.5C41.5 10.4531 32.5482 1.5 21.5029 1.5C10.4577 1.5 1.50586 10.4531 1.50586 21.5C1.50586 22.2969 1.55273 23.0781 1.64646 23.8516L5.30218 22.7734C5.27093 22.3516 5.25531 21.9297 5.25531 21.5C5.25531 12.5234 12.5277 5.25 21.5029 5.25C30.4782 5.25 37.7505 12.5234 37.7505 21.5Z' fill='white' fill-opacity='0.4'%3E%3Canimate attributeName='fill-opacity' begin='0.1s' values='0;0.4;0.4;0' keyTimes='0; 0.2; 0.85; 1' dur='4s' repeatCount='indefinite'/%3E%3CanimateTransform attributeName='transform' type='scale' values='0 0; 1 1; 1 1; 0 0' keyTimes='0; 0.1; 0.9; 1' begin='0.1s' dur='4s' repeatCount='indefinite'/%3E%3C/path%3E%3Cdiv xmlns='' style='all: initial !important;'/%3E%3C/svg%3E");
  background-size: contain;
  background-repeat: no-repeat;
}

.btn[aria-grabbed="true"] {
  transform: scale(1.025);
  box-shadow: inset 0px 0px 1px rgba(0,0,0,0.5), -1px 0 15px 0 rgba(34, 33, 81, 0.01), 0px 15px 15px 0 rgba(34, 33, 81, 0.25)
}
`.trim();

export const CodeSandbox = ({files, height, hero, previewHeight, showTabs, template}) => {
  const [Editor, setEditor] = useState(null);
  useEffect(() => {
    import('@components/CodeSandbox').then(mod => {
      setEditor(() => mod.CodeSandbox);
    });
  }, []);
  if (!Editor) return null;
  return <div className={`not-prose${hero ? ' hero' : ''}`} style={previewHeight ? {
    '--preview-height': `${previewHeight}px`
  } : undefined}>
      <Editor files={files} height={height} showTabs={showTabs} template={template} />
    </div>;
};

export const Story = ({id, framework = "react", width = "100%", height = "250", hero = false}) => {
  const BRANCH = 'experimental';
  const STORYBOOKS = {
    react: {
      localPort: 6006,
      productionHost: '5fc05e08a4a65d0021ae0bf2'
    },
    vue: {
      localPort: 6008,
      productionHost: '6989440ed560d70abcd6bcc7'
    },
    vanilla: {
      localPort: 6007,
      productionHost: '69892d294eb9040f0d29aa81'
    },
    solid: {
      localPort: 6009,
      productionHost: '698944444eb9040f0d2a0217'
    },
    svelte: {
      localPort: 6010,
      productionHost: '69910d2a631cb57638616dcd'
    }
  };
  const config = STORYBOOKS[framework] ?? STORYBOOKS.react;
  const isDev = import.meta.env.DEV;
  const host = isDev ? `//localhost:${config.localPort}` : `https://${BRANCH}--${config.productionHost}.chromatic.com`;
  return <Frame>
      <iframe src={`${host}/iframe.html?args=&id=${id}&viewMode=story&hero=${hero}`} width={width} height={height} />
    </Frame>;
};

## Usage

First, create a [DragDropManager](/concepts/drag-drop-manager) instance to orchestrate the drag and drop system. Then use the `Draggable` class to make elements draggable:

export const code = `
import {Draggable, DragDropManager} from '@dnd-kit/dom';

export function App() {
  const manager = new DragDropManager();

  const element = document.createElement('button');
  element.innerText = 'draggable';
  element.classList.add('btn');

  const draggable = new Draggable({
    id: 'draggable-1', // Required - must be unique
    element,
  }, manager);

  document.body.appendChild(element);
}
`.trim();

<CodeSandbox
  files={{
'index.js': {code: `import './styles.css';\nimport {App} from './draggable.js';\n\nApp();`, hidden: true},
'draggable.js': {code, active: true},
'styles.css': {code: draggableStyles, hidden: true},
}}
  height={360}
  previewHeight={200}
  template="vanilla"
  hero
/>

## Handles

By default, the entire element can be used to initiate dragging. You can restrict dragging to a specific handle element:

```js theme={null}
const element = document.createElement('div');
const handle = document.createElement('div');
handle.classList.add('handle');
handle.innerHTML = '⋮'; // Three dots menu icon for drag handle

element.appendChild(handle);

const draggable = new Draggable({
  id: 'draggable-1',
  element,
  handle, // Only allow dragging from the handle
}, manager);
```

## Types

You can assign types to draggable elements to restrict which [droppable targets](/concepts/droppable) they can be dropped on:

```js theme={null}
// Assign a type
const draggable = new Draggable({
  id: 'draggable-1',
  element,
  type: 'item', // Only droppables accepting 'item' type will be valid targets
}, manager);
```

## Feedback

You can customize how the element behaves while being dragged using the [Feedback](/extend/plugins/feedback) plugin's per-entity configuration:

```js theme={null}
import {Draggable, DragDropManager, Feedback} from '@dnd-kit/dom';

const draggable = new Draggable({
  id: 'draggable-1',
  element,
  plugins: [Feedback.configure({ feedback: 'clone' })],
}, manager);
```

Available feedback options:

* `'default'`: The original element moves with the drag (best for most cases)
* `'clone'`: A copy of the element stays in place while the original moves (good for drag-to-copy)
* `'move'`: The element moves without a placeholder (minimal visual feedback)
* `'none'`: No visual feedback (useful for custom drag overlays)

## API Reference

### Arguments

The `Draggable` class accepts the following arguments:

<ParamField path="id" type="string | number" required>
  A unique identifier for this draggable element within the same [drag and drop context provider](/concepts/drag-drop-manager).
</ParamField>

<ParamField path="element" type="Element">
  The DOM element to make draggable. While not required in the constructor, it must be set to enable dragging.
</ParamField>

<ParamField path="handle" type="Element">
  Optionally specify a drag handle element. If not provided, the entire element will be draggable. See [drag handles](#handles).
</ParamField>

<ParamField path="type" type="string | number | Symbol">
  Optionally assign a type to restrict which droppable targets can accept this element. See [types](#types).
</ParamField>

<ParamField path="plugins" type="PluginDescriptor[]">
  An array of plugin descriptors for per-entity plugin configuration. Use `Plugin.configure()` to create descriptors. See [feedback](#feedback).
</ParamField>

<ParamField path="disabled" type="boolean">
  Set to `true` to temporarily prevent dragging this element.
</ParamField>

<ParamField path="modifiers" type="Modifier[]">
  An array of [modifiers](/extend/modifiers) to customize drag behavior.
</ParamField>

<ParamField path="sensors" type="Sensors[]">
  An array of [sensors](/extend/sensors) to detect drag interactions.
</ParamField>

<ParamField path="alignment" type="{ x: 'start' | 'center' | 'end'; y: 'start' | 'center' | 'end' }">
  How the draggable's position should be aligned relative to its source element during drag operations. Used by the [Feedback plugin](/extend/plugins/feedback) when computing overlay placement. Defaults are inferred from the element's geometry.
</ParamField>

<ParamField path="data" type="{[key: string]: any}">
  Optional data to associate with this draggable element, available in event handlers.
</ParamField>

<ParamField path="effects" type="() => Effect[]">
  <Info>This is an advanced feature and should not need to be used by most consumers.</Info>
  You can supply a function that returns an array of reactive effects that can be set up and automatically cleaned up when invoking the `destroy()` method of this instance.
</ParamField>

### Properties

The `Draggable` instance provides these key properties:

* `id`: The unique identifier
* `element`: The main DOM element
* `handle`: The drag handle element (if specified)
* `type`: The assigned type
* `disabled`: Whether dragging is disabled
* `isDragging`: Whether this element is currently being dragged
* `isDropping`: Whether this element is being dropped

### Methods

* `register()`: Register this draggable with the manager
* `unregister()`: Remove this draggable from the manager
* `destroy()`: Clean up this draggable instance and remove all listeners
