Show other users' cursors on the page


This page has LiveCursors enabled. Try opening this page again in a second window, and notice how the cursors synchronize between the two windows. Try resizing one and notice how the cursors track the logical parts of the page, not just their coordinates!

(LiveCursors doesn't usually show you your own cursor. That's been specially enabled on this page just for demo purposes.)

Live Demo

When to use #

LiveCursors renders the cursors of other users who are on the same page that you are on. They move as the other users move their mouse, and adjust for things like different screen sizes and slightly different DOM structure.

This component is great for giving a more closely collaborative feel to pages where users are all looking at the same thing and would benefit from being able to see what others are pointing at. Examples of such cases might be a document, set of charts, or canvas.

This component is not well-suited to pages where different users might have very different things on the same page. It's not useful to know what someone is pointing at if that thing might not be on your screen at all!


How to use #

The component renders the cursors as fixed-position and so can be placed anywhere in the DOM.

React:
import { LiveCursors } from "@cord-sdk/react";

export function Example() {
  return (
    <LiveCursors location={{ page: 'my-page' }} groupId='my-group' />
  );
}
Vanilla JavaScript:
<cord-live-cursors
  location='{"page": "my-page"}'
  group-id='my-group'
></cord-live-cursors>
import { LiveCursors } from "@cord-sdk/react";

export function Example() {
  return (
    <LiveCursors location={{ page: 'my-page' }} groupId='my-group' />
  );
}
Copy

Source code #

Live Cursors is built on top of our presence API. The complete source code is available here under a permissive license. You can use it to learn from as an example, or even copy-paste into your own app to remix and build a custom experience.

Github's logoView Source Code


Properties #


location #

required
string

Location for the cursors. LiveCursors syncs everyone's cursors at the same Location.

Required unless useCordLocation was used to set the location for the entire page.



groupId #

required
string

The group which should be able to see the user's live cursor.



showViewerCursor #

optional
boolean
Whether to show the viewer their own cursor. Defaults to false.


cursorComponent #

optional
React component

A React component to use to render each user's cursor. The component will be given two props:

  • user: the user whose cursor this is. This is the same set of data returned by useUserData.
  • pos: the position of the user's cursor in viewport-relative coordinates.

If unset, defaults to the cursor used in the demo on this page (a colored arrow with the user's name below it).



translations #

optional
object

Used to customize the logic translating mouse events into logical coordinates that can be sent to other users, and then to translate those logical coordinates into viewport coordinates to display on those other users' screens.

As an example, you might want to override this if you are building an infinite canvas app. You'd translate the mouse events into logical coordinates in your canvas, and then translate those logical coordinates back into viewport coordinates so that cursors show up in the right logical place on the canvas.

Specifying these translations is optional. The default implementation uses viewportCoordinatesToString and stringToViewportCoordinates, which works well for most apps.

This is an object with two fields:

  • eventToLocation. A function which takes one argument, a MouseEvent, and returns a Location representing the logical coordinates of that event, or null, representing an invalid location. This Location, if returned, will be sent to all of the other users to tell them where to put your cursor on their screens.
  • locationToDocument. A function which takes one argument, a Location, and returns an object with two fields, viewportX and viewportY. This translates the Location received from another user into viewport-relative x/y coordinates. That user's cursor is placed onto the screen at those coordinates.


sendCursor #

optional
boolean

Whether to share the cursor position. If false, the cursor position will not be sent to the server. This can be useful if you want some users to see others' cursors, but not share their own cursor position. To control fetching and rendering cursors, please see the showCursors prop.

If unset, defaults to true.



showCursors #

optional
boolean

Whether to fetch and render all other users' cursors. If false, no cursors will be rendered. This can be useful if you want some users to still send their cursor information to others, but not see the others' cursors themselves. To control sharing the cursor position, please see the sendCursor prop.

If unset, defaults to true.

Note: This is a toggle to either show all other cursors currently sharing position information or to show no cursors at all.



boundingElementRef #

optional
React MutableRefObject<HTMLElement | null>

Restrict the live cursor interaction area. Pass in a ref to a element to limit interactions inside it. This prevents both cursor data being sent as well as the rendering of cursors outside of the bounding element.

If unset, live cursors will function across the whole page.



clickComponent #

optional
React component

A React component to use to render each user's cursor click. The component will be given two props:

  • user: the user whose cursor this is. This is the same set of data returned by useUserData.
  • pos: the position of the user's cursor in viewport-relative coordinates.

If unset, defaults to a pulsing circle (a colored circle that grows and shrinks around the cursor).



sendClicks #

optional
boolean

Whether to share the cursor click event. If true, the cursor clicks will be sent to the server. To control fetching and rendering cursor clicks, please see the showClicks prop.

If unset, defaults to false.



showClicks #

optional
boolean

Whether to fetch and render all other users' clicks. If true, an element is rendered at the click position. A default click element is provided that can be customized via a class name. To control sharing the cursor click position, please see the sendClicks prop.

If unset, defaults to false.

Note: This is a toggle to either show all other cursors currently sharing click information or to show no cursor clicks at all.



clickDisplayDuration #

optional
number

Controls how long, in milliseconds, a click is shown before being cleared.

If unset, defaults to 1000 ms.


CSS customization #

If you want to customize this component, you can target the classes below in your app's CSS. These are guaranteed to be stable.

Class nameDescription
.cord-live-cursors-cursor
Applied to the cursor container div containing the cursor icon and cursor label elements.
.cord-live-cursors-icon
Applied to the cursor icon SVG element.
.cord-live-cursors-label
Applied to the div containing the Cord user Avatar and user name.
.cord-live-cursors-name
Applied to the element containing the user name

Color palettes #

The color palettes used for each cursor is determined by CSS variables set by a cord-color-palette-X class, where X is a number between 1 and 8. This number is unique per user ID. This means that the same user ID will always be assigned the same palette number. So, if the user with ID "Jack" has a cord-color-palette-2 in LiveCursors, it will also have the same palette in Avatar, and in every other Cord component.
Here's a CSS code snippet you could use to customize cursors using the color palette class:

CSS:
.cord-live-cursors-cursor.cord-color-palette-4 .cord-icon {
  color: deeppink;
}

.cord-live-cursors-cursor.cord-color-palette-4 .cord-live-cursors-label {
  background-color: deeppink;
  border: 1px solid coral;
}
              
.cord-live-cursors-cursor.cord-color-palette-4 .cord-icon {
  color: deeppink;
}

.cord-live-cursors-cursor.cord-color-palette-4 .cord-live-cursors-label {
  background-color: deeppink;
  border: 1px solid coral;
}
              
Copy

Not finding the answer you need? Ask our Developer Community

Ask Cordy