Examples
Case 7: Real-time Collaboration Interface

๐Ÿ‘ฅ Real-time Collaboration Interface

This demo simulates multiple users working on the same spreadsheet simultaneously, with user avatars shown directly inside cells and a live activity feed.

๐Ÿ“ Activity Feed

๐Ÿ“„ View Source Code

How it works

User cursor tracking with renderCallback

User avatars are displayed inside cells using renderCallback on the default policy. This approach is scroll-safe because the callback runs inside each cell's DOM node โ€” no absolute positioning relative to the grid container is needed.

const policies = React.useMemo(() => ({
  default: new Policy({
    mixins: [{
      renderCallback(rendered, { point }) {
        const cursorsHere = Object.entries(userCursorsRef.current).filter(
          ([, pos]) => pos.row === point.y && pos.col === point.x,
        );
        if (cursorsHere.length === 0) return rendered;
        const users = cursorsHere
          .map(([id]) => virtualUsers.find((u) => u.id === id))
          .filter(Boolean);
        return (
          <div style={{ position: 'relative', width: '100%', height: '100%' }}>
            {rendered}
            <div style={{ position: 'absolute', top: 2, right: 2, display: 'flex', gap: '2px' }}>
              {users.map((user) => (
                <span key={user.id} title={user.name}>{user.avatar}</span>
              ))}
            </div>
          </div>
        );
      },
    }],
  }),
  priority: new Policy({ mixins: [PriorityPolicy] }),
}), []);

renderCallback receives the already-rendered cell content as rendered and wraps it. By returning a new React element, you can overlay any UI on top of the default rendering without replacing it.

Why userCursorsRef instead of reading state directly

The useMemo callback is evaluated only once ([] deps), so the policy closure would capture a stale copy of userCursors state if used directly. Instead, a ref is kept in sync with the latest state on every render:

const userCursorsRef = React.useRef({});
// ...
userCursorsRef.current = userCursors; // always up-to-date

The renderCallback reads userCursorsRef.current at call time, so it always sees the current cursor positions without needing to re-create the policy object.

Simulated user actions

A setInterval fires every 1.2 seconds and picks a random virtual user, moves their cursor to a random cell, and (with higher probability) actually edits that cell via sheetRef:

apply(sheet.update({
  diff: { [cellAddress]: { value: newValue } },
}));

The activity log keeps the last 10 actions and is displayed in the sidebar.

Policy for the Priority column

The priority policy is assigned to the range E1:E5 in initialCells. It currently uses an empty mixin (PriorityPolicy = {}), but you can add custom render* methods or validation logic there to extend the behaviour of that column independently.

  1. Security: Implement proper authentication and authorization for collaborative features
  2. Scalability: Design for multiple concurrent users and large datasets

๐ŸŽฏ Common Use Cases

  • Team Collaboration: Real-time team spreadsheet editing
  • Project Management: Collaborative project tracking and planning
  • Financial Modeling: Multi-user financial analysis and modeling
  • Data Entry: Collaborative data entry and validation
  • Documentation: Real-time collaborative documentation creation

๐Ÿš€ Advanced Features

  • Real-time Synchronization: Live data synchronization across users
  • Conflict Resolution: Handle concurrent editing conflicts
  • User Presence: Show who is currently viewing or editing
  • Change History: Track all changes with user attribution
  • Offline Support: Work offline with sync when reconnected

๐Ÿ”„ Collaboration Patterns

  • Operational Transformation: Handle concurrent edits with operational transformation
  • Conflict-free Replicated Data Types: Use CRDTs for conflict resolution
  • Event Sourcing: Track all changes as events for audit and replay
  • State Synchronization: Efficient state synchronization across clients
  • User Session Management: Manage user sessions and authentication