--- title: Cursors created_at: 01/31/2026 updated_at: 01/31/2026 keywords: - cursor - pointer - setCursor - rotation - color - presence - collaborator status: published date: 01/31/2026 --- The cursor system controls what cursor users see when interacting with the canvas. The current cursor is stored in instance state and changes automatically as users hover over different elements or use different tools. You can also set the cursor manually for custom tools. ## Cursor state The cursor state is a [TLCursor](?): a [TLCursorType](?) and a rotation angle. Access it through [Editor#getInstanceState](?): ```typescript const { type, rotation } = editor.getInstanceState().cursor ``` The `type` determines the visual appearance—like `'default'`, `'grab'`, or `'nwse-resize'`. The `rotation` is an angle in radians that rotates the cursor icon. Rotation is mainly used for resize and rotate cursors so they align with the shape being manipulated. ## Cursor types tldraw supports these cursor types: | Type | Description | | ------------- | ---------------------------------------------- | | `default` | Standard pointer arrow | | `pointer` | Hand indicating clickable element | | `cross` | Crosshair for precise positioning | | `comment` | Speech bubble for placing comments | | `grab` | Open hand for draggable content | | `grabbing` | Closed hand while dragging | | `text` | I-beam for text editing | | `move` | Four-way arrow for moving elements | | `zoom-in` | Magnifying glass with plus | | `zoom-out` | Magnifying glass with minus | | `ew-resize` | Horizontal resize (east-west) | | `ns-resize` | Vertical resize (north-south) | | `nesw-resize` | Diagonal resize (northeast-southwest) | | `nwse-resize` | Diagonal resize (northwest-southeast) | | `nesw-rotate` | Rotation handle (northeast-southwest position) | | `nwse-rotate` | Rotation handle (northwest-southeast position) | | `senw-rotate` | Rotation handle (southeast-northwest position) | | `swne-rotate` | Rotation handle (southwest-northeast position) | | `none` | Hidden cursor | Static cursors like `default`, `pointer`, and `grab` are prerendered SVG cursors defined as CSS custom properties in tldraw's stylesheet (`--tl-cursor-default`, `--tl-cursor-grab`, and so on). Dynamic cursors like the resize and rotate types are generated at runtime as SVGs with the current rotation applied. The canvas reads the result from the `--tl-cursor` variable. > `TLCursorType` also includes `resize-edge`, `resize-corner`, and `rotate` for schema compatibility. The default cursor rendering doesn't support them, so don't pass them to `setCursor`. ## Setting the cursor Use [Editor#setCursor](?) to change the cursor: ```typescript editor.setCursor({ type: 'cross', rotation: 0 }) ``` You can update just the type or just the rotation—the other property keeps its current value: ```typescript // Change only the type editor.setCursor({ type: 'grab' }) // Change only the rotation editor.setCursor({ rotation: Math.PI / 4 }) ``` ### Cursor rotation Rotation is specified in radians. When users resize or rotate shapes that are themselves rotated, the cursor rotates to match: ```typescript // Get the selection's rotation and apply it to a resize cursor const selectionRotation = editor.getSelectionRotation() editor.setCursor({ type: 'nwse-resize', rotation: selectionRotation, }) ``` This keeps the cursor aligned with the shape's edges rather than the screen axes. The default tools handle cursor rotation automatically. You only need to set it manually for custom tools. ## Cursors in custom tools Custom tools typically set the cursor when entering a state and reset it when exiting: ```typescript import { StateNode } from 'tldraw' export class MyCustomTool extends StateNode { static override id = 'my-tool' override onEnter() { this.editor.setCursor({ type: 'cross', rotation: 0 }) } override onExit() { this.editor.setCursor({ type: 'default', rotation: 0 }) } } ``` For tools with child states, each state can set its own cursor. A drawing state might use `'cross'`, while a dragging state uses `'grabbing'`. ## Cursor colors Dynamic cursors (resize and rotate types) receive the active [theme](/sdk-features/themes)'s `cursor` color for the current color mode: black in the default light theme, white in the default dark theme. The built-in SVGs use fixed black and white fills for contrast, so this color only shows in custom cursor SVGs. ## Collaborator cursors In multiplayer sessions, each user's cursor appears on other users' canvases. These remote cursors use the user's presence color—a randomly assigned color from the user color palette. ### User color palette When a user first loads tldraw, they're assigned a random color from the built-in `USER_COLORS` palette: ```typescript const USER_COLORS = [ '#FF802B', '#EC5E41', '#F2555A', '#F04F88', '#E34BA9', '#BD54C6', '#9D5BD2', '#7B66DC', '#02B1CC', '#11B3A3', '#39B178', '#55B467', ] ``` You can read or change a user's color through user preferences: ```typescript // Get the user's color const color = editor.user.getColor() // Set a specific color editor.user.updateUserPreferences({ color: '#FF802B' }) ``` ### Rendering collaborator cursors Remote cursors render as DOM elements in a dedicated layer stacked above the canvas and below the UI panels. Each visible collaborator gets a cursor showing: - The cursor arrow in the user's color - The user's name as a label next to the cursor - Any active chat message in a bubble A collaborator whose cursor is outside your viewport shows as a small hint arrow clamped to the viewport edge, pointing toward them. [CollaboratorHintOverlayUtil](?) draws the hints on the overlay canvas rather than as DOM elements. To customize the cursor, pass your own component via the `components` prop's `CollaboratorCursor` slot (see [DefaultCursor](?) and [TLCursorProps](?)): ```tsx import { TLCursorProps, Tldraw } from 'tldraw' import 'tldraw/tldraw.css' function CustomCollaboratorCursor({ point, color, name, zoom }: TLCursorProps) { if (!point) return null return (