Skip to content
Tools— browse docs
On this page

Step 6: See their cursors, live

A facepile with useDocumentPresence, and a real remote pointer moving over your own canvas.

Data converging is most of the way there. The last thing a spatial tool wants is to see who's there — a facepile in the header, exactly like Getting Started's text board got, using the same hook:

function PresenceStrip({ handle }) {
  const others = useDocumentPresence(handle).filter((p) => !p.isSelf)
  if (others.length === 0) return null
  return (
    <span style={{ display: "flex" }}>
      {others.map((p) => (
        <span key={p.userId} style={{ background: p.color /* … */ }}>
          {p.name.slice(0, 1).toUpperCase()}
        </span>
      ))}
    </span>
  )
}

Pull handle out of useDocument and thread it into Board, next to the pin count.

Add a facepile
src/tool.tsx Lines 29 to 33 and 120 highlighted.

Edit and the example re-runs. Tab indents; press Escape to leave the editor.

Running · your edits, live
Starting the example…

You should see: nothing changes yet — there's only one of you in this pane. Presence lives in the awareness channel, never your document, so it has nothing to show until someone else shows up.

Here's someone else showing up — two real identities, each with their own cursor, both looking at the same board:

Two people, one board
Move your pointer inside a pane
Starting the example…

Move your pointer inside a pane — the other one draws it, right over the board, not just somewhere on the page. Drop a pin and the coloured chip appears in both headers, because both panes are running your PresenceStrip, reading the same useDocumentPresence(handle).

That's the whole reason a spatial tool's presence reads differently from a text tool's: the cursor isn't decorating a line of prose, it's a second person's hand on the same board as yours.

Recap

  • useDocumentPresence(handle) lists everyone in the document right now — build a facepile from it with no channel of your own to manage.
  • Presence is ephemeral — it lives in the awareness channel, never your document — so a lone pane shows nothing until someone else arrives.
  • The remote cursor you just watched move is real: a genuine second Awareness, relayed live, never simulated.

What you built

A document-backed pin board: drop a pin at the exact pixel you clicked, drag one without drift, mount the same component full-size or card-sized via context.view, and watch two people work it at once — data and cursors both, live. Six steps, no sync code written by hand.

Next steps