All hooks / Events and input
useCaretPosition
The caret's position on screen in a <textarea> or text <input>, or null while the field isn't focused. Returns a callback ref for the field.
Browsers don't expose caret coordinates, so this measures a hidden copy of the field's text and styles. Updates on typing, clicks, arrow keys, selection changes, and scrolling inside the field or the page, at most once per frame. Coordinates are in viewport pixels, for a position: fixed popup. Safe with controlled fields: it never updates state during their events.
Import
import { useCaretPosition } from '@vincent.ri/react-tils'
// or only this hook
import { useCaretPosition } from '@vincent.ri/react-tils/useCaretPosition'Usage
const [ref, caret] = useCaretPosition()
const [text, setText] = useState('')
const mentioning = /@\w*$/.test(text)
return (
<>
<textarea ref={ref} value={text} onChange={(e) => setText(e.target.value)} />
{mentioning && caret && (
<ul style={{ position: 'fixed', top: caret.top + caret.height, left: caret.left }}>…</ul>
)}
</>
)Live demo
Type @ anywhere in the text: the popup follows the caret.
When to use it
Placing an @mention, /command, emoji, or autocomplete popup next to the caret, like in chat and comment inputs.
When not to
The popup can sit below the whole field; position it from the field's own box. For rich text editors, use the editor's selection API; for text selected elsewhere on the page, use useTextSelection.
API
function useCaretPosition<T extends HTMLTextAreaElement | HTMLInputElement = HTMLTextAreaElement>(): [(element: T | null) => void, CaretPosition | null]Returns [ref, position]. Pass ref to the textarea or input.
Types
export type CaretPosition = {
/** Top of the caret line, in viewport pixels. Use with `position: fixed`. */
top: number;
/** Left of the caret, in viewport pixels. */
left: number;
/** Line height at the caret, in pixels. Place a popup at `top + height`. */
height: number;
};Browser support
| Uses | Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS |
|---|---|---|---|---|---|---|
| Pointer eventsWidely available | 55+ | 12+ | 59+ | 13+ | 55+ | 13+ |
| selectionchange eventWidely available | 11+ | 12+ | 52+ | 5.1+ | 18+ | 5+ |
| requestAnimationFrameWidely available | 24+ | 12+ | 23+ | 7+ | 25+ | 7+ |
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).
Coming from another library?
Other libraries call this useCaretCoordinates, textarea-caret.