# Collaborative Presence
URL: https://pro.rv-grid.com/guides/data-manage/collaborative-presence/
Source: src/content/docs/guides/data-manage/collaborative-presence.mdx
Description: Render remote collaborator focus cells, selected ranges, labels, colors, stale-user cleanup, and source-index row patches in RevoGrid Pro.
`CollaborativePresencePlugin` shows where other users are looking or editing inside the grid. It renders colored focus rectangles, selected ranges, avatars, and user labels without changing the local user's focus, selection, editor, data history, or keyboard state.

Use it by itself when your app already has presence data, or use it with [Collaborative Editing](/guides/data-manage/collaborative-editing/) when Yjs awareness should drive remote cursors and ranges automatically.

## What It Does

    Show the single cell another user is focused on.

    Show another user's selected or active range.

    Render initials, names, and stable colors so users can identify each other quickly.

    Resolve marker positions from RevoGrid viewport state, including pinned columns, instead of querying physical cell DOM nodes.

Presence markers are overlays. They do not move local focus, create local selections, open editors, or persist data by themselves.

## Basic Setup

1. Import and register the plugin.

   ```ts
   import {
     CollaborativePresencePlugin,
   } from '@revolist/revogrid-pro';

   grid.plugins = [
     CollaborativePresencePlugin,
   ];
   ```

2. Provide users through `grid.collaborativePresence`.

   ```ts
   grid.collaborativePresence = {
     users: [
       {
         id: 'avery',
         name: 'Avery Stone',
         initials: 'AS',
         color: '#2563eb',
         activity: 'editing',
         focus: { x: 0, y: 1 },
         range: { x: 0, y: 1, x1: 2, y1: 3 },
         lastActiveAt: Date.now(),
       },
       {
         id: 'grace',
         name: 'Grace Hopper',
         initials: 'GH',
         color: '#16a34a',
         activity: 'viewing',
         focus: { x: 1, y: 0 },
         lastActiveAt: Date.now(),
       },
     ],
   };
   ```

The plugin observes `grid.collaborativePresence`, `grid['collaborative-presence']`, and `additionalData.collaborativePresence`.

## Cell And Range Coordinates

Presence coordinates use grid source indexes:

- `x` is the source column index in the target column area.
- `y` is the source row index in the target row area.
- `x1` and `y1` are the opposite range corner.
- `colType` defaults to `'rgCol'`.
- `rowType` defaults to `'rgRow'`.

```ts
grid.collaborativePresence = {
  users: [
    {
      id: 'avery',
      name: 'Avery Stone',
      color: '#2563eb',
      focus: { x: 0, y: 2 },
      range: { x: 0, y: 2, x1: 3, y1: 4 },
    },
  ],
};
```

For pinned columns, pass the matching column type. This keeps the marker in the pinned viewport instead of the main viewport.

```ts
grid.collaborativePresence = {
  users: [
    {
      id: 'grace',
      name: 'Grace Hopper',
      color: '#16a34a',
      focus: {
        x: 0,
        y: 1,
        colType: 'colPinStart',
        rowType: 'rgRow',
      },
    },
  ],
};
```

The plugin resolves source indexes through RevoGrid stores and viewport structures. This is important when sorting, filtering, pinning, or virtualization changes which physical cells are mounted.

## Configuration Reference

| Property | Type | Default | What it controls |
|:--|:--|:--|:--|
| `enabled` | `boolean` | `true` | Turns all presence markers on or off without removing the plugin. |
| `showLabels` | `boolean` | `true` | Shows or hides user name labels. Focus and range colors still render when labels are hidden. |
| `staleAfterMs` | `number` | `undefined` | Hides users whose `lastActiveAt` is older than the configured timeout. |
| `users` | `CollaborativePresenceUser[]` | `[]` | The remote users to render. Each user can provide `focus`, `range`, or both. |
| `remoteEdits` | `CollaborativePresenceRowPatch[]` | `[]` | Optional source-row patches for remote data changes that should update grid rows without resetting focus or selection. |

## User Shape

```ts
type CollaborativePresenceUser = {
  /**
   * Stable user id used to distinguish one collaborator from another.
   * Use your application user id, not a temporary session id or display name.
   */
  id: string;

  /**
   * Human-readable name shown in the presence label when `showLabels` is true.
   */
  name: string;

  /**
   * Optional short avatar text. If omitted, the label can derive initials from `name`.
   */
  initials?: string;

  /**
   * Optional marker color used for focus borders, range overlays, avatar, and label.
   * Keep this stable per user so collaborators are easy to recognize.
   */
  color?: string;

  /**
   * Optional activity state for your own presence model.
   * The plugin accepts viewing, editing, and idle states.
   */
  activity?: 'viewing' | 'editing' | 'idle';

  /**
   * Optional focused cell for this user.
   * `x` and `y` are source indexes in the specified column and row areas.
   * Set this to `null` when the user has no active focus to render.
   */
  focus?: {
    /** Source column index inside `colType`. */
    x: number;

    /** Source row index inside `rowType`. */
    y: number;

    /**
     * Column area that owns the focused cell.
     * Defaults to `rgCol`; use pinned column types for pinned cells.
     */
    colType?: 'colPinStart' | 'rgCol' | 'colPinEnd';

    /**
     * Row area that owns the focused cell.
     * Defaults to `rgRow`; use pinned row types for pinned rows.
     */
    rowType?: 'rgRow' | 'rowPinStart' | 'rowPinEnd';
  } | null;

  /**
   * Optional selected or active range for this user.
   * `x`, `y` and `x1`, `y1` are opposite corners in source indexes.
   * Set this to `null` when the user has no range to render.
   */
  range?: {
    /** Source column index for one range corner. */
    x: number;

    /** Source row index for one range corner. */
    y: number;

    /** Source column index for the opposite range corner. */
    x1: number;

    /** Source row index for the opposite range corner. */
    y1: number;

    /**
     * Column area that owns the range.
     * Defaults to `rgCol`; use pinned column types for pinned ranges.
     */
    colType?: 'colPinStart' | 'rgCol' | 'colPinEnd';

    /**
     * Row area that owns the range.
     * Defaults to `rgRow`; use pinned row types for pinned ranges.
     */
    rowType?: 'rgRow' | 'rowPinStart' | 'rowPinEnd';
  } | null;

  /**
   * Last known activity time for stale-user cleanup.
   * Accepts a timestamp, date string, or Date object.
   * Used only when `staleAfterMs` is configured.
   */
  lastActiveAt?: number | string | Date;
};
```

Use stable `id` values and consistent colors so the same person looks the same across grids and sessions.

## Labels And Stale Users

Use `showLabels: false` for dense grids where full labels would cover too much content.

```ts
grid.collaborativePresence = {
  showLabels: false,
  users,
};
```

Use `staleAfterMs` to hide users who disconnect or stop sending presence updates.

```ts
grid.collaborativePresence = {
  staleAfterMs: 15_000,
  users: remoteUsers.map(user => ({
    ...user,
    lastActiveAt: user.lastSeenAt,
  })),
};
```

## Remote Row Patches

`remoteEdits` can apply remote row updates directly into the RevoGrid data store without resetting the full source or disturbing the local editor state.

```ts
grid.collaborativePresence = {
  remoteEdits: [
    {
      rowIndex: 2,
      rowType: 'rgRow',
      data: {
        status: 'Approved',
        owner: 'Grace',
      },
    },
  ],
  users,
};
```

`rowIndex` is a source row index, not the currently visible row position. That keeps patches stable when the grid is sorted or filtered.

## With Collaborative Editing

When `CollaborativeEditingPlugin` has presence enabled, it maps Yjs awareness state into `CollaborativePresencePlugin`.

```ts
grid.plugins = [
  EventManagerPlugin,
  CollaborativePresencePlugin,
  CollaborativeEditingPlugin,
];

grid.collaborativeEditing = {
  documentId: `budget:${budgetId}`,
  user: currentUser,
  rowIdProp: 'id',
  presence: {
    enabled: true,
    showLabels: true,
    staleAfterMs: 15_000,
  },
};
```

Use direct `grid.collaborativePresence` updates when your app owns the realtime presence source. Use collaborative editing presence when Yjs awareness owns the realtime presence source.
