How To Add Live Cursors Into Any React App

This guide will walk you through building a real-time collaborative cursor application using React and AirState. By the end of this tutorial, you'll have created an application where users can see each other's cursor positions in real-time, similar to collaborative tools like Figma or Miro.
Prerequisites
- Basic knowledge of ReactJS
- Node.js and pnpm/yarn installed
- Familiarity with Javascript and JSX
Project Setup (Vite + Tailwind)
We're using a React application built with Vite and styled with Tailwind CSS. While we won't go into extensive detail on this initial setup, here's a quick overview of the structure we'll be working with:
live-cursor/
├── src/
│ ├── pages/
│ │ └── cursor.jsx # Cursor component
│ ├── App.jsx # Main application component
│ └── main.jsx # Entry point
├── package.json
└── vite.config.js
With this structure in place, we can now focus on implementing the real-time cursor functionality with AirState.
AirState Integration
Installation
AirState is a powerful library that enables real-time collaboration features in web applications. Let's start by installing the required packages:
pnpm install @airstate/client @airstate/react
These packages provide:
@airstate/client
: The core client library for AirState@airstate/react
: React-specific hooks and components for AirState
Configure using AirState Cloud
Login to AirState Cloud Console and create an App. Then copy the appId
which is required to configure AirState.
simply import and configure your client application on App.jsx
import { configure } from '@airstate/client';
configure({
appId: '[YOUR CLOUD APP ID]',
});
useSharedPresence
hook
The key to our real-time cursor implementation is the useSharedPresence
hook from AirState. The hook provides several important values:
self
- Your own peer informationsetState
- Function to update your local state, which is then shared with othersothers
- Object containing information about other connected peers and their states
How Collaboration Works
- Each user joins the same room (we’ll call it
"myCollaborationRoom"
) with a uniquepeerId
. - On mouse movement, we publish
{ x, y }
AirState handles the real-time sync—no manual WebSocket plumbing required. We’ll render small indicators for connected peers using peer.connected
to show who’s online.
Creating The Component
Now let's explore what's happening in our Cursor component. The magic starts with the useSharedPresence
hook that connects our component to the AirState network:
const { others, setState } = useSharedPresence({
peerId: uuidv4(),
room: "myCollaborationRoom",
initialState: { x: 0, y: 0 },
});
When we initialize this hook, we're essentially saying "Hey, I want to join the 'myCollaborationRoom' room." Think of the room like a virtual space where collaboration happens. The initialState
provides our starting cursor position. The hook gives us back two important things: others
(information about everyone else in the room) and setState
(our way to broadcast updates).
As the user moves their mouse around the screen, we want to share that movement with everyone else. We capture this with a simple mouse event
<div
className="w-screen h-screen bg-blue-500"
onMouseMove={(e) => {
setState({
x: e.clientX,
y: e.clientY,
});
}}
></div>
Every time the mouse moves, we call setState
with our updated position. It's like telling everyone in the room, "Hey, I'm now at these coordinates!" AirState handles all the networking complexity - we just update our state and it makes sure everyone else knows about it.
The final piece is showing where everyone else is. We take the others
object (which contains all the other connected peers), filter it to only show active connections, and render a small red dot for each person:
{Object.values(others).filter((peer)=> peer.connected).map((peer) => (
<div
key={peer.peerId}
className="w-2 h-2 bg-red-500 absolute rounded-full"
style={{
position: "absolute",
left: peer.state.x - 1,
top: peer.state.y - 1,
}}
></div>
))}
Each dot is positioned exactly where that person's cursor is. The peer.connected
check ensures we only show people who are currently online. As people move their cursors, these dots will move in real-time, creating that collaborative feeling similar to what you'd experience in tools like Figma or Google Docs
🚀 Complete Implementation
Below is our complete implementation of the real-time collaborative cursor feature. This elegant solution uses just a few lines of code to create a powerful multi-user experience.
import React from "react";
import { useSharedPresence } from "@airstate/react";
import { v4 as uuidv4 } from "uuid";
const Cursor = () => {
const { others, setState } = useSharedPresence({
peer: uuidv4(),
room: "myCollaborationRoom",
initialState: { x: 0, y: 0},
});
return (
<>
<div
className="w-screen h-screen bg-blue-500"
onMouseMove={(e) => {
setState({
x: e.clientX,
y: e.clientY,
});
}}
></div>
{Object.values(others).filter((peer)=> peer.connected).map((peer) => (
<div
key={peer.peer}
className="w-2 h-2 bg-red-500 absolute rounded-full"
style={{
position: "absolute",
left: peer.state.x - 1,
top: peer.state.y - 1,
}}
></div>
))}
</>
);
};
export default Cursor;
💻 Try it yourself*: Copy this code into your project, install the dependencies, and open two browser windows to see the cursors interact in real-time!*
Conclusion
Just run the application using pnpm run dev
and you’ve now built a real-time collaborative cursor application using React and AirState! The useSharedPresence
hook from AirState makes it simple to create collaborative experiences without having to manage WebSocket connections, state synchronization, or peer-to-peer communication yourself.
Happy Coding!