The problem
My team tracked work in a shared spreadsheet that kept breaking with merge conflicts and gave no real-time visibility into who was doing what. I wanted a lightweight board that felt as fast as a spreadsheet but updated live for everyone.
Role and impact
- Individual project, full-stack.
- Replaced a shared spreadsheet for a 6-person team.
- Sub-second drag-and-drop sync across clients via Supabase Realtime.
- 30 tests covering board state and permission rules.
Technical decisions and challenges
The board needed to reflect other users' moves instantly without the local drag interaction jumping or resetting mid-gesture. I compared applying remote updates directly to local state versus queuing them until the local drag finished.
I ended up queuing remote updates during an active local drag and flushing them right after drop, since applying them mid-gesture caused visible jank on slower connections.
Two people reordering the same column at once could produce conflicting card positions. I evaluated last-write-wins versus a fractional-indexing scheme.
I chose fractional indexing for card order, because it avoids full-column rewrites and resolves most concurrent moves without a visible "snap back."
Detailed stack
| Layer | Technology | Why |
|---|---|---|
| Frontend | Next.js 15, dnd-kit, Tailwind | dnd-kit gives accessible drag-and-drop without reinventing it |
| Backend | Supabase (Postgres + Realtime, RLS) | Live sync out of the box, per-board access control with RLS |
| Testing | Vitest, React Testing Library | Coverage for board state, permission rules, and reordering logic |
Gallery
What I would do differently
I would add offline support from day one instead of assuming a stable connection. The current implementation drops queued moves if the connection is lost mid-drag, which is a real limitation for spotty office wifi.