Svelte
sh
npm install @anil-labs/dnd-svelteWorks with Svelte 4 and Svelte 5 (runes) alike.
The use:draggable action
You render the list with {#each}; the action wires the engine to the container:
svelte
<script lang="ts">
import { draggable } from '@anil-labs/dnd-svelte'
import '@anil-labs/dnd-core/styles.css'
let items = $state([
{ id: 1, text: 'Apple' },
{ id: 2, text: 'Banana' },
{ id: 3, text: 'Cherry' },
])
</script>
<ul
use:draggable={{
items,
onItemsChange: (v) => (items = v),
group: 'tasks',
animation: 200,
onEnd: (e) => console.log('dropped', e.oldIndex, '→', e.newIndex),
}}
>
{#each items as item, i (item.id)}
<li data-dnd-index={i} class="dnd-item">{item.text}</li>
{/each}
</ul>Three rules for the markup:
- Items are keyed direct children (
(item.id)) so Svelte moves nodes instead of recreating them. - Each item carries
data-dnd-index={i}matching its array position. - Each item has the
dnd-itemclass (cursor +touch-action).
Options
items and onItemsChange plus everything in the options reference — including all event callbacks (onStart, onEnd, onAdd, onRemove, onUpdate, onChange, onSelectionChange, onKeyboardMove, …).
The action's update runs whenever the options object changes, so reactive values Just Work:
svelte
<ul use:draggable={{ items, onItemsChange: (v) => (items = v), disabled: locked }}>Svelte 4
Identical, with store/let state instead of runes:
svelte
<script>
import { draggable } from '@anil-labs/dnd-svelte'
let items = [{ id: 1, text: 'Apple' }]
</script>
<ul use:draggable={{ items, onItemsChange: (v) => (items = v), animation: 200 }}>
{#each items as item, i (item.id)}
<li data-dnd-index={i} class="dnd-item">{item.text}</li>
{/each}
</ul>Selection & keyboard state
Track them via callbacks and render whatever you like:
svelte
<script lang="ts">
let selected = $state<number[]>([])
</script>
<ul
use:draggable={{
items,
onItemsChange: (v) => (items = v),
multiDrag: true,
onSelectionChange: (e) => (selected = e.indices),
}}
>
{#each items as item, i (item.id)}
<li data-dnd-index={i} class="dnd-item" class:picked={selected.includes(i)}>
{item.text}
</li>
{/each}
</ul>For keyboard mode add tabindex="0" to items (or rely on the engine's announcements only); the dnd-keyboard-active outline is applied via onKeyboardStateChange if you mirror it into a class the same way.