All hooks / Events and input

useTextSelection

351 B minified and brotliedSourceMarkdown✓ Works in all modern browsers

The text the user has selected inside an element, or null. Returns a callback ref for the element.

Follows mouse, touch, and keyboard selection (Shift+arrows, select all). While the mouse or finger is still down it waits, so a toolbar doesn't flicker during a drag, and updates on release. null when the selection is empty, only whitespace, or not entirely inside the element.

Import

import { useTextSelection } from '@vincent.ri/react-tils'
// or only this hook
import { useTextSelection } from '@vincent.ri/react-tils/useTextSelection'

Usage

const [ref, selection] = useTextSelection<HTMLElement>()
return (
  <>
    <article ref={ref}>{children}</article>
    {selection && (
      <div style={{ position: 'fixed', top: selection.rect.top - 40, left: selection.rect.left }}>
        <button onClick={() => quote(selection.text)}>Quote</button>
      </div>
    )}
  </>
)

Live demo

Select some text with the mouse or keyboard.

Hooks for browser state that render the right first frame. Double-click anything here.

selected: —

When to use it

A floating toolbar for highlight, comment, share quote, or "Ask AI about this", or a "copy quote" button.

When not to

The text is in an <input> or <textarea>; read its selectionStart and selectionEnd. For a rich text editor, use the editor's own selection API. rect doesn't follow scrolling; read range.getBoundingClientRect() again if the page scrolls.

API

function useTextSelection<T extends Element = HTMLElement>(): [(element: T | null) => void, TextSelection | null]

Returns [ref, selection]. Pass ref to the element containing the text.

Types

export type TextSelection = {
  /** The selected text. */
  text: string;
  /** Where the selection is on screen, in viewport coordinates, for placing a toolbar. */
  rect: DOMRect;
  /** A copy of the selected range, e.g. to wrap it in a highlight. */
  range: Range;
};

Browser support

UsesChromeEdgeFirefoxSafariChrome AndroidSafari iOS
Pointer eventsWidely available55+12+59+13+55+13+
getSelectionWidely available1+12+1+1+18+1+
selectionchange eventWidely available11+12+52+5.1+18+5+

First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).