4 logged considerations
react-dragdrop-kit
Controlled drag-and-drop primitives for React: sortable lists and grids, plus a headless Kanban path, built on pragmatic-drag-and-drop and shipped as a tree-shakeable npm package with a live demo.
3 structural threads
3 collected signals
Jump to section
Case briefing
The work started from repeat product needs: sortable task lists, grid-like reordering, and Kanban-style moves across columns. Each surface was small on its own, but constraints were tight—no heavy runtime, behaviour had to stay predictable under React re-renders, and consumer apps needed to enforce their own rules (permissions, optimistic UI, server sync). Publishing as a library meant the demo site and README had to prove behaviour, not only API shape.
Initial signal
What broke first
Every new sortable surface reimplemented the same pointer-event and reorder wiring with slightly different state assumptions—when to commit order (live versus on drop), how positions were stored, and how Kanban state was normalised—so fixes on one screen did not transfer, and edge cases (handles, multi-select, horizontal lists) kept diverging.
Evidence collected
Controlled reorder contract
Hosts implement `onReorder(newItems, orderUpdates)` and own persistence; the toolkit does not silently mutate app data, which keeps audit and server sync in one place.
Kanban state model
Normalised board state plus `applyDragResult` keeps column/card moves explicit and testable without relying on implicit library state.
Shipped documentation surface
The demo site and examples folder match the README quick starts so behaviour can be validated before install.
Investigation path
Tradeoff 01Wrapper depth vs. auditable integration
High-level DnD libraries that hide state shrink boilerplate but make permission checks, optimistic UI, and server reconciliation harder to trace. pragmatic-drag-and-drop keeps mechanics predictable while the host owns data shape and persistence boundaries.
Tradeoff 02Explicit callbacks over silent mutation
The package rejects mutating consumer stores internally. That trades a slightly larger integration surface (`onReorder`, `onDragEnd`) for compile-time-friendly boundaries and a single place to audit writes.
Tradeoff 03Kanban as normalised state + pure transition
A columns/cards map with `applyDragResult` lets the same move logic run in tests or server reconciliation without mounting React—useful when board moves must stay consistent across tabs or retries.
- Log 01
Compared staying on high-level wrappers versus building on lower-level primitives. Full-stack DnD libraries that own internal state made app-level rules harder to audit; `@atlaskit/pragmatic-drag-and-drop` provided stable building blocks without dictating data shape, which fit a controlled, app-owned model.
- Log 02
Ruled out hiding state inside the package: it would shrink boilerplate but blur where persistence and validation run. The tradeoff accepted was more explicit callbacks (`onReorder`, `onDragEnd`) in exchange for traceable integration with app stores and APIs.
- Log 03
Validated the Kanban shape as a normalised map (`columns` + `cards`) plus a pure reducer-style step (`applyDragResult`) so moves within and across columns stay testable without mounting React.
- Log 04
Treated the demo app and Medium write-up as part of the delivery: adoption depended on seeing list, multi-drag, handle-only, and Kanban paths in one place.
Resolution
- Action 01
Ship a controlled list API: items carry `id` and `position`; the library reports reorder results and optional `orderUpdates`, and the host updates whatever store it uses.
- Action 02
Expose Kanban as a separate entry point (`react-dragdrop-kit/kanban`) so list-only bundles do not pay for board code, matching the published size split (~5KB list, ~9KB Kanban in the package docs).
- Action 03
Document migration cues from `react-beautiful-dnd` (context/droppable/draggable → board + render props) so teams evaluating migration can map concepts, not guess.
- Action 04
Keep TypeScript types at the boundary (`DropResult`, `KanbanBoardState`) so consumer refactors surface mistakes during compile rather than at runtime.
- Structure 01
Data flow is strictly controlled: React state (or an external store) holds item arrays or Kanban state; components emit drag lifecycle and drop results upward. For Kanban, `applyDragResult(stateBefore, result)` applies a drop to immutable state so the same transition logic can be reused in tests or server reconciliation.
- Structure 02
Component boundaries split flat list/grid (`DragDropList`) from the Kanban surface (`KanbanBoard`, column/card views). Shared mechanics sit on pragmatic-drag-and-drop, which keeps pointer and collision handling out of product code while leaving rendering to `renderItem` / `renderColumn` / `renderCard`.
- Structure 03
The repository is structured as a publishable package plus `apps/demo`: the demo is the behavioural contract—examples for Tailwind, MUI, Kanban variants, and accessibility helpers (`AnnouncerProvider`)—so the published API is exercised the way consumers would use it.
Outcome
The package ships on npm with the demo deployed to Netlify; list and Kanban entry points stay tree-shakeable, and the Medium article documents the design intent alongside the repository. Integration cost for a new sortable surface is mostly wiring `onReorder` or `onDragEnd` to existing state, rather than re-solving pointer behaviour for that screen.