Real-Time Multiplayer To-do list with Fractional Indexing

Tags
Node.js
React.js
Web Dev
Projects
Next.js
Netlify
Fullstack
TailwindCSS
Supabase
Published
December 13, 2025
Author
Kalen Wallin
api_v1_role
api_v1_category
api_v1_company
api_v1_type
api_v1_year
api_v1_cover
api_v2_order
api_v3_link
Talk

The Problem

While working on a new feature at work that required sort order, one question haunted me: What is the best way to handle sort order for a list of drag and drop items that is accessed by multiple users in real-time?

The Journey

Step 1: Researching Sort Order Management

I started by diving deep into various approaches for managing sort order in drag-and-drop interfaces. The naive approach—simply using integer indices (1, 2, 3, 4...)—requires renumbering all subsequent items when you insert something in the middle. This becomes especially problematic in a collaborative environment where multiple users are making changes simultaneously.

Step 2: Discovering Fractional Indexing

My research led me to an elegant solution: fractional indexing. This technique uses strings that represent fractional values between items, allowing you to insert new items between any two existing items without renumbering anything else.
I found several invaluable resources:
Evan Wallace's Articles (creator of esbuild)
Evan wrote two foundational articles that changed how I think about this problem:
  1. Fractional Indexing in Peer-to-Peer Networks - This article explores how to implement fractional indexing in distributed systems without a central authority.
  1. Real-time Editing of Ordered Sequences - Evan explains how Figma handles real-time collaborative editing of ordered lists using fractional indexing in a centralized architecture.
npm Packages
Two npm packages caught my attention:
  1. fractional-indexing - A solid implementation inspired by Evan's work. The Observable notebook explanation provides an excellent deep dive into the implementation details.
  1. jittered-fractional-indexing - An extension of fractional-indexing that adds random jitter functionality. This is crucial for minimizing index collisions when multiple users generate fractional indices concurrently.
How Fractional Indexing Works
Instead of using integers (1, 2, 3, 4), fractional indexing uses lexicographically ordered strings:
  • Items start with indices like: "a0", "a1", "a2"
  • To insert between "a0" and "a1", you generate: "a0V"
  • To insert between "a0V" and "a1", you generate: "a0h"
The beauty is that you can always generate a string that falls between any two existing strings, and the sorting is done lexicographically. The jitter functionality adds randomness to reduce the probability that two users will generate the exact same index at the same time.

Step 3: Building the Drag-and-Drop Interface

With a solid understanding of fractional indexing, I built a Next.js application with a drag-and-drop interface similar to a product backlog. The key features:
  • Drag items to reorder them
  • Each item gets a fractional index instead of an integer position
  • When an item is dropped, calculate a new fractional index between its neighbors
  • Update the item's position using the generated index

Step 4: Adding Local Sync with Dexie

To provide a seamless experience across browser tabs, I integrated Dexie.js, a wrapper around IndexedDB. This allows:
  • Persisting the list locally in the browser
  • Syncing changes between multiple tabs of the same browser
  • Instant UI updates without waiting for network requests
  • Offline-first functionality
When a user drags an item:
  1. Update the in-memory state
  1. Save to IndexedDB via Dexie
  1. Broadcast the change to other tabs

Step 5: Adding Cloud Sync with Supabase

To enable collaboration across devices and users, I integrated Supabase:
  • Store all items in a Postgres database
  • Each item has its fractional index stored as a string column
  • Sorting is done with a simple ORDER BY fractional_index
The database schema is straightforward:
CREATE TABLE items ( id UUID PRIMARY KEY, content TEXT, fractional_index TEXT NOT NULL, created_at TIMESTAMP, updated_at TIMESTAMP ); CREATE INDEX idx_items_fractional_index ON items(fractional_index);

Step 6: Implementing Real-Time Sync with Supabase Realtime

The final piece of the puzzle was adding real-time collaboration using Supabase Realtime:
  • Subscribe to database changes using Postgres CDC (Change Data Capture)
  • When any user updates an item, all connected clients receive the change instantly
  • Each client updates its local state and UI automatically
  • The fractional indexing ensures that even concurrent reorders don't conflict
The result is a truly multiplayer experience where multiple users can drag and drop items simultaneously, and everyone sees the changes in real-time.

The Result

The combination of these technologies creates a robust, real-time collaborative drag-and-drop experience:
No conflicts - Fractional indexing eliminates position conflicts
Real-time updates - Supabase Realtime keeps everyone in sync
Local-first - Dexie provides instant feedback and offline support
Scalable - No need to renumber items when reordering
Concurrent-safe - Jittered indices minimize collision probability

Key Takeaways

  1. Fractional indexing is the right solution for collaborative ordered lists - It elegantly solves the concurrent reordering problem.
  1. Jitter matters - The random jitter in jittered-fractional-indexing significantly reduces collision probability in high-concurrency scenarios.
  1. Local-first architecture improves UX - Using IndexedDB with Dexie makes the app feel instant, even with network latency.
  1. Supabase makes real-time easy - The built-in real-time functionality handles the complexity of broadcasting changes to all clients.
  1. Order with strings, not integers - Storing sort order as lexicographically ordered strings is more flexible than integer-based approaches.
Building this application taught me that the right data structure (fractional indices) combined with the right tools (Supabase, Dexie, jittered-fractional-indexing) can transform a seemingly complex problem into an elegant solution.

This application can be found at https://fractin.netlify.app/. Go ahead and add some items to the list. Use multiple browser tabs and devices to feel the power of Dexie and Supabase at your fingertips!

Demos

Items

notion image

Comments

notion image