Skip to content
Domphy

Drag & Drop

@domphy/dnd adds sortable lists and drag & drop to Domphy: reorder, transfer between lists, multi-drag, and drop animations.

This package depends on @formkit/drag-and-drop (MIT, zero-dependency, framework-agnostic) and adds a thin Domphy adapter — the same way FormKit's own React/Vue/Solid adapters wrap the engine. The full FormKit API is re-exported.

Install

npm install @domphy/dnd @domphy/core
<script src="https://unpkg.com/@domphy/dnd/dist/dnd.global.js"></script>

@domphy/core is a peer dependency.

Live Example

Usage

Apply dragDrop(state, config?) to the list container with $, and render the children reactively from the same state with a stable _key:

import { toState } from "@domphy/core"
import { dragDrop } from "@domphy/dnd"

const items = toState([
  { id: 1, label: "Write docs" },
  { id: 2, label: "Ship it" },
])

const App = {
  ul: (l) => items.get(l).map((item) => ({ li: item.label, _key: item.id })),
  $: [dragDrop(items)],
}

Dragging reorders the DOM, FormKit calls setValues → the items state updates → Domphy re-renders the keyed children in the new order. The _key is required so the reorder maps to the right nodes.

Config & plugins

dragDrop(state, config) forwards config to FormKit's ParentConfig, and the whole FormKit API (plugins like animations, insert, dropOrSwap, group transfer) is re-exported. Animations are on by default:

import { dragDrop } from "@domphy/dnd"

// animated by default; transfer items between any lists sharing a group
{ ul: (l) => ..., $: [dragDrop(items, { group: "todos" })] }

Give two lists the same group to transfer items between them. Use animated: false to opt out of animations. Touch and synthetic-drag handling come from FormKit — see the FormKit DnD docs for the full config.

Keyboard

The FormKit engine is pointer-only. Add keyboardSort() to each item for the keyboard path WCAG 2.2 SC 2.5.7 requires — space/enter picks an item up, arrows move it, escape cancels, and every step is announced:

import { dragDrop, keyboardSort } from "@domphy/dnd"

const sortItem = keyboardSort(items)

const App = {
  ul: (l) =>
    items.get(l).map((item, index) => ({
      li: item.label,
      _key: item.id,
      $: [sortItem(index)],
    })),
  $: [dragDrop(items)],
}

See Accessibility.

Cleanup

The FormKit registration lives in a per-node behavior() instance; on element removal its destroy runs FormKit's tearDown(), disconnects the setup MutationObserver upstream leaves running, and clears the entry from FormKit's parents registry.