agentsclimarketplace

Web dnd dnd kit

Skill agents-inc/skills/dist/plugins/web-dnd-dnd-kit/skills/web-dnd-dnd-kit

Drag and drop with @dnd-kit - draggable, droppable, sortable, collision detection, sensors, accessibilityFrom its SKILL.md

Install
npx -y skills add agents-inc/skills --skill web-dnd-dnd-kit

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 20 stars20 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

18.1 KB, ~4.3k tokens by cl100k_base, as published. Nobody here has run it

@dnd-kit Drag and Drop Patterns

Quick Guide: Use @dnd-kit/core for basic drag/drop (useDraggable, useDroppable, DndContext). Use @dnd-kit/sortable for sortable lists (useSortable, SortableContext, arrayMove). Use DragOverlay for cross-container drag, scrollable containers, and smooth drop animations. Configure sensors for pointer/touch/keyboard input with activation constraints. Always provide keyboard and screen reader accessibility via KeyboardSensor and custom announcements.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST wrap all drag-and-drop content in a <DndContext> provider -- hooks only work inside DndContext)

(You MUST use DragOverlay when items move between containers or live in scrollable containers -- transform alone breaks in these cases)

(You MUST configure KeyboardSensor with sortableKeyboardCoordinates for sortable lists -- keyboard users cannot reorder without it)

(You MUST keep DragOverlay always mounted and conditionally render its children -- unmounting DragOverlay breaks drop animations)

(You MUST use named constants for all activation constraints, distances, and timing values -- NO magic numbers)

</critical_requirements>


Auto-detection: @dnd-kit, dnd-kit, DndContext, useDraggable, useDroppable, useSortable, SortableContext, DragOverlay, useSensors, useSensor, PointerSensor, KeyboardSensor, closestCenter, closestCorners, rectIntersection, pointerWithin, arrayMove, sortableKeyboardCoordinates, CSS.Transform, @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities, @dnd-kit/modifiers

When to use:

  • Building sortable lists (reorderable todo, playlist, sidebar navigation)
  • Building Kanban boards with cross-container item movement
  • Implementing drag handles for specific activation areas
  • Creating droppable zones (file upload targets, trash bins, category bins)
  • Adding keyboard and screen reader accessible drag interactions

When NOT to use:

  • Simple reordering without drag UX (use array manipulation + buttons instead)
  • Drag interactions that only need native HTML5 drag-and-drop (e.g., file drops from OS)
  • Complex physics-based drag (consider a gesture/spring animation library instead)

Key patterns covered:

  • DndContext + useDraggable + useDroppable for basic drag/drop
  • SortableContext + useSortable + arrayMove for sortable lists
  • DragOverlay for cross-container drag and smooth animations
  • Sensor configuration (pointer, touch, keyboard) with activation constraints
  • Collision detection strategies (closestCenter, closestCorners, pointerWithin, rectIntersection)
  • Sorting strategies (vertical, horizontal, rect/grid)
  • Keyboard and screen reader accessibility
  • Multi-container sortable (Kanban boards)
  • Modifiers for axis locking and boundary constraints

Detailed Resources:

  • examples/core.md - DndContext, useDraggable, useDroppable, useSortable, sensors, collision detection, accessibility
  • examples/advanced.md - Multi-container Kanban, DragOverlay, modifiers, custom collision detection
  • reference.md - Decision frameworks, API quick reference, sorting strategies, anti-patterns

<philosophy>

Philosophy

@dnd-kit is a modular, lightweight drag-and-drop toolkit for React built around hooks. It separates concerns into focused packages: @dnd-kit/core for the drag/drop primitives, @dnd-kit/sortable for list reordering, @dnd-kit/utilities for CSS transform helpers, and @dnd-kit/modifiers for movement constraints.

Core principles:

  1. Hooks-first -- useDraggable, useDroppable, and useSortable keep drag logic colocated with components
  2. Sensor-driven input -- Pointer, touch, and keyboard inputs are separate sensor plugins, not hardcoded behavior
  3. Collision detection is pluggable -- Choose the right algorithm for your layout (list vs grid vs stacked containers)
  4. Accessibility by default -- Built-in ARIA attributes, keyboard navigation, and screen reader announcements
  5. No DOM manipulation -- Uses CSS transforms for positioning, not DOM reordering during drag

Package overview:

PackagePurpose
@dnd-kit/coreDndContext, useDraggable, useDroppable, DragOverlay, sensors, collision detection
@dnd-kit/sortableSortableContext, useSortable, sorting strategies, arrayMove, sortableKeyboardCoordinates
@dnd-kit/utilitiesCSS.Transform.toString, CSS.Transition.toString
@dnd-kit/modifiersrestrictToVerticalAxis, restrictToHorizontalAxis, restrictToParentElement, restrictToWindowEdges
</philosophy>
<patterns>

Core Patterns

Pattern 1: Basic Drag and Drop

DndContext is the provider that connects draggable and droppable elements. useDraggable makes an element draggable. useDroppable makes an element a drop target.

import { DndContext, type DragEndEvent } from "@dnd-kit/core";

function App() {
  const [parent, setParent] = useState<string | null>(null);

  function handleDragEnd(event: DragEndEvent) {
    const { over } = event;
    setParent(over ? String(over.id) : null);
  }

  return (
    <DndContext onDragEnd={handleDragEnd}>
      <DraggableItem id="item-1" />
      <DroppableZone id="zone-a">{parent === "zone-a" && <span>Dropped here</span>}</DroppableZone>
    </DndContext>
  );
}

Why good: DndContext wraps all participants, event handler updates state on drop, draggable and droppable use unique string IDs

See examples/core.md Pattern 1 for full useDraggable and useDroppable implementations with TypeScript types.


Pattern 2: Sortable Lists

SortableContext + useSortable provides reorderable lists. Use arrayMove from @dnd-kit/sortable to update state on drag end.

import { DndContext, closestCenter, type DragEndEvent } from "@dnd-kit/core";
import { SortableContext, arrayMove, verticalListSortingStrategy } from "@dnd-kit/sortable";

function SortableList() {
  const [items, setItems] = useState(["a", "b", "c", "d"]);

  function handleDragEnd(event: DragEndEvent) {
    const { active, over } = event;
    if (over && active.id !== over.id) {
      setItems((prev) => {
        const oldIndex = prev.indexOf(String(active.id));
        const newIndex = prev.indexOf(String(over.id));
        return arrayMove(prev, oldIndex, newIndex);
      });
    }
  }

  return (
    <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        {items.map((id) => <SortableItem key={id} id={id} />)}
      </SortableContext>
    </DndContext>
  );
}

Why good: closestCenter is forgiving for vertical lists, verticalListSortingStrategy optimizes transform calculations, arrayMove produces a new array (immutable)

See examples/core.md Pattern 2 for the full SortableItem component using useSortable and CSS.Transform.


Pattern 3: DragOverlay

Use DragOverlay instead of transforming the dragged element directly when items move between containers, live in scrollable/virtualized containers, or need custom drag previews.

import { DndContext, DragOverlay, type DragStartEvent, type DragEndEvent } from "@dnd-kit/core";

function Board() {
  const [activeId, setActiveId] = useState<string | null>(null);

  return (
    <DndContext
      onDragStart={(event: DragStartEvent) => setActiveId(String(event.active.id))}
      onDragEnd={(event: DragEndEvent) => {
        handleDragEnd(event);
        setActiveId(null);
      }}
    >
      {/* containers and sortable items */}
      <DragOverlay>{activeId ? <ItemPreview id={activeId} /> : null}</DragOverlay>
    </DndContext>
  );
}

Key rules: Keep DragOverlay always mounted (conditionally render children, not the component). Children rendered inside DragOverlay must NOT use useDraggable. Default drop animation is 250ms ease -- disable with dropAnimation={null}.

See examples/advanced.md Pattern 1 for DragOverlay with sortable lists and custom drop animations.


Pattern 4: Sensors and Activation Constraints

Sensors control which input methods activate dragging. Use useSensors to compose multiple sensors with activation constraints.

import { useSensor, useSensors, PointerSensor, KeyboardSensor } from "@dnd-kit/core";
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";

const ACTIVATION_DISTANCE_PX = 8;

const sensors = useSensors(
  useSensor(PointerSensor, {
    activationConstraint: { distance: ACTIVATION_DISTANCE_PX },
  }),
  useSensor(KeyboardSensor, {
    coordinateGetter: sortableKeyboardCoordinates,
  }),
);

<DndContext sensors={sensors}>{/* ... */}</DndContext>

Why good: distance constraint prevents accidental drags on click, KeyboardSensor with sortableKeyboardCoordinates enables arrow-key reordering, named constant for distance threshold

Sensor types: PointerSensor (unified pointer events), MouseSensor (mouse only), TouchSensor (touch with delay support), KeyboardSensor (arrow keys + Space/Enter)

See examples/core.md Pattern 3 for all sensor configurations including touch delay and tolerance.


Pattern 5: Collision Detection

Choose the collision algorithm based on your layout.

AlgorithmImportBest for
rectIntersection@dnd-kit/coreGeneral drop zones (default)
closestCenter@dnd-kit/coreSortable lists -- forgiving, no overlap required
closestCorners@dnd-kit/coreStacked/overlapping containers (Kanban columns)
pointerWithin@dnd-kit/corePrecision drop -- pointer must be inside target

Gotcha: pointerWithin only works with pointer-based sensors. Compose it with a fallback for keyboard support.

See examples/core.md Pattern 4 for collision detection selection and custom composition.


Pattern 6: Sorting Strategies

Choose the strategy based on list orientation.

StrategyImportUse case
rectSortingStrategy@dnd-kit/sortableGrids (default, does NOT support virtualized)
verticalListSortingStrategy@dnd-kit/sortableVertical lists (supports virtualized)
horizontalListSortingStrategy@dnd-kit/sortableHorizontal lists (supports virtualized)
rectSwappingStrategy@dnd-kit/sortableSwap mode (items trade positions)

Always match the strategy to your layout -- using rectSortingStrategy on a vertical list produces suboptimal animations.


Pattern 7: Keyboard and Screen Reader Accessibility

@dnd-kit provides built-in accessibility. useDraggable applies role="button", aria-roledescription="draggable", tabindex="0", and links to screen reader instructions via aria-describedby.

Customize announcements via the announcements prop on DndContext:

const announcements = {
  onDragStart({ active }: { active: { id: UniqueIdentifier } }) {
    return `Picked up item ${active.id}`;
  },
  onDragOver({ active, over }: { active: { id: UniqueIdentifier }; over: { id: UniqueIdentifier } | null }) {
    if (over) return `Item ${active.id} moved over ${over.id}`;
    return `Item ${active.id} is no longer over a drop target`;
  },
  onDragEnd({ active, over }: { active: { id: UniqueIdentifier }; over: { id: UniqueIdentifier } | null }) {
    if (over) return `Item ${active.id} dropped on ${over.id}`;
    return `Item ${active.id} was dropped`;
  },
  onDragCancel({ active }: { active: { id: UniqueIdentifier } }) {
    return `Dragging cancelled. Item ${active.id} was dropped`;
  },
};

<DndContext announcements={announcements}>{/* ... */}</DndContext>

Why good: Screen readers announce drag state changes in real time, position-based messages ("position 2 of 5") are more useful than generic "moved over" messages

See examples/core.md Pattern 5 for position-based announcements and custom screen reader instructions.


Pattern 8: Multi-Container Sortable (Kanban)

For Kanban boards, each column has its own SortableContext. Items move between containers via onDragOver (update state as item crosses boundaries) and onDragEnd (finalize position).

Key decisions for multi-container:

  • Use closestCorners collision detection (handles stacked columns better than closestCenter)
  • Use DragOverlay (items unmount from source container during cross-container drag)
  • Track activeId to render the drag preview in the overlay
  • Use onDragOver for real-time container transfers, onDragEnd for final placement

See examples/advanced.md Pattern 2 for the complete Kanban implementation.


Pattern 9: Modifiers

Modifiers constrain drag movement. Apply to DndContext (affects all dragging) or DragOverlay (affects overlay only).

import { restrictToVerticalAxis, restrictToParentElement } from "@dnd-kit/modifiers";

<DndContext modifiers={[restrictToVerticalAxis]}>{/* ... */}</DndContext>
ModifierPackageEffect
restrictToVerticalAxis@dnd-kit/modifiersLock movement to Y axis
restrictToHorizontalAxis@dnd-kit/modifiersLock movement to X axis
restrictToParentElement@dnd-kit/modifiersConstrain to parent bounds
restrictToWindowEdges@dnd-kit/modifiersPrevent dragging outside viewport

Different modifiers can be applied to DndContext and DragOverlay independently.

</patterns>

<decision_framework>

Decision Framework

Which Package Do I Need?

Do you need sortable lists?
|-- YES -> @dnd-kit/core + @dnd-kit/sortable (+ @dnd-kit/utilities for CSS.Transform)
+-- NO  -> Just drag/drop zones?
    +-- YES -> @dnd-kit/core only

Transform vs DragOverlay

Do items move between containers?
|-- YES -> Use DragOverlay (items unmount from source during drag)
+-- NO  -> Is the draggable inside a scrollable/virtualized container?
    |-- YES -> Use DragOverlay (avoids overflow clipping)
    +-- NO  -> Do you need a custom drag preview different from the source?
        |-- YES -> Use DragOverlay
        +-- NO  -> Transform approach is sufficient (simpler)

Collision Detection

Single sortable list?
|-- YES -> closestCenter (forgiving, no overlap needed)
+-- NO  -> Stacked containers (Kanban columns)?
    |-- YES -> closestCorners (better for overlapping droppables)
    +-- NO  -> Precision drop targets (trash bin, category bins)?
        |-- YES -> pointerWithin (only triggers when pointer is inside)
        +-- NO  -> rectIntersection (default, general purpose)

</decision_framework>


<red_flags>

RED FLAGS

High Priority Issues:

  • Missing DndContext wrapper -- useDraggable/useDroppable/useSortable fail silently without it
  • Conditionally mounting/unmounting DragOverlay -- breaks drop animations; always mount it, conditionally render children
  • Missing KeyboardSensor -- keyboard users cannot interact with drag-and-drop at all
  • Using closestCenter for stacked containers (Kanban) -- often selects the column instead of items within; use closestCorners
  • Forgetting sortableKeyboardCoordinates on KeyboardSensor for sortable lists -- arrow keys move by pixels instead of to next item
  • Mutating state in onDragEnd instead of producing new arrays -- arrayMove returns a new array; do not use .splice() directly on state

Medium Priority Issues:

  • Using rectSortingStrategy (default) for vertical-only lists -- verticalListSortingStrategy is more performant and supports virtualization
  • Missing activation constraints on PointerSensor -- accidental drags fire on every click
  • Not providing custom announcements -- default messages use IDs which are meaningless to screen reader users
  • Applying useDraggable inside DragOverlay children -- the overlay renders a preview, not an interactive draggable

Gotchas & Edge Cases:

  • useDraggable and useDroppable can share the same id (they use separate stores), but useSortable combines both so its id must be unique across draggables AND droppables
  • SortableContext items prop must match the order of rendered children -- mismatches cause animation glitches
  • pointerWithin only works with pointer-based sensors -- compose with closestCenter fallback for keyboard support
  • CSS.Transform.toString() returns undefined when transform is null -- safe to pass directly to style.transform
  • Transform values include scaleX/scaleY -- if you don't want scaling, destructure and only use x/y with CSS.Translate.toString()
  • DragOverlay is NOT rendered in a portal by default -- use createPortal if you need it to escape overflow/stacking contexts
  • The data argument on useDraggable/useDroppable is available in event handlers via active.data.current and over.data.current -- useful for carrying metadata (type, container ID)
  • arrayMove is a pure utility -- it does not update state; you must call your setter with its return value
  • Screen reader instructions default to English only -- provide screenReaderInstructions prop for localization

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST wrap all drag-and-drop content in a <DndContext> provider -- hooks only work inside DndContext)

(You MUST use DragOverlay when items move between containers or live in scrollable containers -- transform alone breaks in these cases)

(You MUST configure KeyboardSensor with sortableKeyboardCoordinates for sortable lists -- keyboard users cannot reorder without it)

(You MUST keep DragOverlay always mounted and conditionally render its children -- unmounting DragOverlay breaks drop animations)

(You MUST use named constants for all activation constraints, distances, and timing values -- NO magic numbers)

Failure to follow these rules will break drag interactions, keyboard accessibility, and drop animations.

</critical_reminders>

What ships with it: 3 files

31.8 KB alongside SKILL.md

examples/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.