30% offEnding soon
useTextSelectionLoading saved progress…

useTextSelection

A text selection is the run of characters the user has highlighted on the page — what they would copy if they pressed Ctrl+C. useTextSelection is a React hook that reports that selection as plain, live state, updating every time the selection changes. The browser exposes the current selection through window.getSelection() and announces every change with the selectionchange event; your job is to turn that imperative API into React state a component can render.

Signature

function useTextSelection(): {
  text: string;         // the selected text, "" when nothing is selected
  isCollapsed: boolean; // true for no selection or a bare caret (no highlight)
};

The hook takes no arguments — it watches the whole document — and returns a fresh object whenever the selection changes.

Examples

// The user drags across the word "hello" on the page.
const { text, isCollapsed } = useTextSelection();
// text === "hello", isCollapsed === false
// The user clicks once (a caret, no highlight) or clicks away entirely.
// text === "", isCollapsed === true

Notes

  • selectionchange lives on the document. It fires on document, not on any element, and it does not bubble — so document.addEventListener('selectionchange', ...) is the only way to hear it.
  • getSelection() returns one shared object. Every call hands back the same live Selection, which the browser mutates in place. Read the values you need out of it at event time; do not hold onto the object.
  • Collapsed vs ranged. A selection can be collapsed — a caret with no highlighted text — or a real range. Report the two distinctly; isCollapsed is what tells them apart.
  • Clean up. Remove the listener when the component unmounts, or it keeps firing into a component that is gone.
  • Empty is normal, not an error. When there is no selection, report empty text — never crash.
  • Out of scope: selection rectangles and coordinates, selections inside iframes, and programmatic selection on inputs. See the solution's "Going further".