If you’ve ever tried to build a drag-and-drop table in React, you already know the pain.
Standard vertical lists are easy. But the moment you try to build a fully virtualized 2D data grid where users can drag both rows and columns simultaneously, almost every open-source library breaks down.
Usually, one of two things happens:
The Layout Thrash: The library uses React State/Context to track the mouse coordinates, causing 60 re-renders per second across your entire DOM tree.
The Virtualization Crash: The drop zones rely on physical DOM nodes. The second a user scrolls and a virtualized column unmounts, the drag engine loses its reference and the UI breaks.
I got so frustrated with these bottlenecks that I decided to build a highly specialized, bare-metal drag engine specifically for React tables.
It’s called react-table-dnd, and to make it work at 60fps with 100,000+ virtualized rows, I had to stop using React for the heavy lifting.
Here is the architecture of how it works under the hood.
- Ripping out React Context for O(1) Updates Passing drag state down through Context is a performance killer for massive tables. Instead, react-table-dnd uses a custom vanilla JavaScript store hooked up to useSyncExternalStore.
When a user drags a row, only that specific active cell re-renders. The rest of the table stays completely dead-quiet. O(1) rendering.
- Native DOM Cloning (cloneNode) Most libraries use React.cloneElement to create the floating "ghost" you drag around. In a virtualized table, this is a nightmare because it fails to capture the computed widths of auto-sized columns.
Instead, I used the browser's native cloneNode(true). The C++ layout engine takes a pixel-perfect snapshot of the row in a millisecond, giving us an exact replica with zero React overhead.
- Direct DOM Mutations When you are dragging, React doesn't know about it. The drag orchestrator uses direct DOM mutations (style.transform) inside a requestAnimationFrame loop. You are essentially offloading the drag physics directly to the GPU.
Because we bypass the React reconciliation cycle, the library can easily handle 2,000+ fixed DOM rows simultaneously without dropping a single frame, and scales infinitely when paired with @tanstack/react-virtual.
- O(1) Map Caching for 2D Virtualization To fix the "unmounting virtual columns" bug, the engine calculates the initial geometry of the grid and caches the bounding boxes in an O(1) Map. You can drag a row vertically, and immediately drag a column horizontally, and the math engine handles the spacer divs flawlessly.
The Result
Zero UI Dependencies (shipped entirely on scoped CSS)
Mobile-First (native-feeling long-press and momentum scrolling)
Tiny footprint (86kB unpacked)
If you are dealing with enterprise-scale data grids, or if you just want to see how to abuse useSyncExternalStore for raw performance, I'd love for you to tear the code apart or try the interactive demos.
🚀 Interactive 2D Grid Demos: react-table-dnd Documentation
💻 GitHub Repo: https://github.com/samiodeh1337/react-table-dnd
📦 NPM: npm i react-table-dnd
Let me know in the comments how you usually handle massive DnD bottlenecks in React!

Top comments (0)