Remote Focus
Show the single cell another user is focused on.
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 when Yjs awareness should drive remote cursors and ranges automatically.
Remote Focus
Show the single cell another user is focused on.
Remote Ranges
Show another user’s selected or active range.
Labels And Colors
Render initials, names, and stable colors so users can identify each other quickly.
Grid-Aware Geometry
Resolve marker positions from RevoGrid viewport state, including pinned columns, instead of querying physical cell DOM nodes.
Import and register the plugin.
import { CollaborativePresencePlugin,} from '@revolist/revogrid-pro';
grid.plugins = [ CollaborativePresencePlugin,];Provide users through grid.collaborativePresence.
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.
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'.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.
grid.collaborativePresence = { users: [ { id: 'grace', name: 'Grace Hopper', color: '#16a34a', focus: { x: 0, y: 1, colType: 'colPinStart', rowType: 'rgRow', }, }, ],};| 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. |
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.
Use showLabels: false for dense grids where full labels would cover too much content.
grid.collaborativePresence = { showLabels: false, users,};Use staleAfterMs to hide users who disconnect or stop sending presence updates.
grid.collaborativePresence = { staleAfterMs: 15_000, users: remoteUsers.map(user => ({ ...user, lastActiveAt: user.lastSeenAt, })),};remoteEdits can apply remote row updates directly into the RevoGrid data store without resetting the full source or disturbing the local editor state.
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.
When CollaborativeEditingPlugin has presence enabled, it maps Yjs awareness state into CollaborativePresencePlugin.
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.