All articles
ReactWebSocketsTypeScriptArchitecture

Handling Real-Time State in Multiplayer Applications

Learn how to architect real-time state synchronization for multiplayer apps using WebSockets, conflict resolution strategies, and optimistic updates.

March 3, 202618 min read

Introduction

Building multiplayer applications presents unique challenges that single-user apps never face: multiple sources of truth, network latency, conflicting updates, and the need for immediate feedback.

**What you'll build**: A real-time collaborative whiteboard application that handles concurrent edits gracefully.

Prerequisites

  • TypeScript fundamentals
  • React hooks (useState, useEffect, useCallback)
  • Basic WebSocket concepts
  • The Core Problem

    When two users edit the same data simultaneously, you face the classic concurrent update problem:

    typescript
    // User A sends at t=100ms
    { action: 'move', shapeId: '1', x: 100, y: 200 }
    
    // User B sends at t=101ms
    { action: 'move', shapeId: '1', x: 150, y: 250 }
    
    // Which update wins?

    Implementing Optimistic Updates

    Don't wait for server confirmation—update the UI immediately:

    typescript
    const moveShape = (id: string, x: number, y: number) => {
      // Update locally first
      updateLocalState(id, x, y, { optimistic: true });
      
      // Then send to server
      ws.send(JSON.stringify({ type: 'MOVE_SHAPE', id, x, y }));
    };

    Conflict Resolution

    Use a deterministic strategy for resolving conflicts:

    typescript
    function resolveConflict(localOp: Operation, remoteOp: Operation): Operation {
      if (remoteOp.timestamp > localOp.timestamp) return remoteOp;
      if (remoteOp.timestamp === localOp.timestamp) {
        return remoteOp.clientId > localOp.clientId ? remoteOp : localOp;
      }
      return localOp;
    }

    Common Pitfalls

    **Avoid storing WebSocket in React state** — use `useRef` to prevent unnecessary re-renders.

    **Implement reconnection logic** — connections drop; always handle reconnects gracefully.

    Conclusion

    Real-time state management requires careful thought about optimistic updates, conflict resolution, and reconnection handling. Start simple and add complexity only when needed.

    Further Reading

  • [Designing Data-Intensive Applications](https://dataintensive.net/)
  • [Yjs CRDT Documentation](https://docs.yjs.dev/)