Skip to content

useTextSelection

Hook to track text selection.

See: Selection API.

Demo

INFO

The component renders a grid with two columns. First column contains two tag p elements with text and secondo column the result of useTextSelection hook.

useTextSelection is initialized with a target property that has a ref attached to first column div and onEnd property that has a function that clean selection. When text is selected appears colored rectangles with the coordinates returned from hook.

Loading demo…
Show source code
tsx
import { useMemo, useRef } from "react";
import { useTextSelection } from "../../../.."

export const UseTextSelection = () => {
	const ref = useRef<HTMLDivElement>(null);
	const selection = useTextSelection({ target: ref, onEnd: () => {getSelection()?.removeAllRanges()} });
	const rectangles = useMemo(() => {
		if (!selection) {
			return null;
		} else {
			const rectangles = [];
			rectangles.push(
				<div
					key="outside-rectangle"
					style={{
						position: "absolute",
						border: ".5px solid red",
						top: selection.outsideRectangle.top + "px",
						left: selection.outsideRectangle.left + "px",
						width: selection.outsideRectangle.width + "px",
						height: selection.outsideRectangle.height + "px",
					}}
				></div>
			);
			selection.innerRectangles.forEach((el, index) => {
				rectangles.push(<div
					key={"inner-rectangle-"+index}
					style={{
						position: "absolute",
						border: ".5px solid darkcyan",
						top: el.top + "px",
						left: el.left + "px",
						width: el.width + "px",
						height: el.height + "px",
					}}
				></div>);
			})
			return rectangles;
		}
	}, [selection]);

	return <div style={{ display: "grid", gridTemplateColumns: "50% 50%", columnGap: 15 }}>
		<div ref={ref} style={{ position: "relative", border: "1px solid lightgray" }}>
			<div>
				<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt repudiandae fugit distinctio molestiae excepturi ex qui, impedit iste odit. Explicabo quis reprehenderit voluptates reiciendis nostrum minima autem temporibus sint doloribus</p>
			</div>
			<div>
				<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt repudiandae fugit distinctio molestiae excepturi ex qui, impedit iste odit. Explicabo quis reprehenderit voluptates reiciendis nostrum minima autem temporibus sint doloribus</p>
			</div>
			{rectangles}
		</div>
		<div style={{textAlign: "left", padding: "0 1em", maxHeight: 300, overflow: "auto", border: "1px solid lightgray"}}>
			<p><strong>Selection:</strong></p>
			<pre>{JSON.stringify(selection, null, 2)}</pre>
		</div>
	</div>
}

Types

UseTextSelectionProps

Parameters accepted by useTextSelection.

PropertyTypeRequiredDescription
targetRefObject<HTMLElement> \| HTMLElementThe element within which text selection is tracked. Accepts either a React RefObject&lt;HTMLElement&gt; or a direct HTMLElement reference. When omitted, selection is tracked globally on document.body.
onStart(evt: Event) => voidCalled when the user begins a selection gesture inside the target element (on pointerdown). Also fired when the pointer re-enters the target while a selection is already in progress and no text is currently selected.
onChange(evt: Event) => voidCalled on every selectionchange event while a selection gesture is active inside the target element. Registered on document for the duration of the gesture and removed on pointerup or pointerleave.
onEnd(evt: Event) => voidCalled when the selection gesture ends inside the target element (on pointerup or pointerleave), after the final {@link TextSelection} value has been computed and the reactive state has been updated.

UseTextSelectionResult

Text selection state returned by useTextSelection. Updated whenever the user changes the text selection in the document.

ts
export type UseTextSelectionResult = null | {
	/** The raw text content of the current selection (equivalent to `Selection.toString()`). */
	text: string;
	/**
	 * Direction of the selection relative to the anchor point.
	 * - `"forward"` — the user dragged from left to right / top to bottom.
	 * - `"backward"` — the user dragged from right to left / bottom to top.
	 */
	direction: "forward" | "backward";
	/**
	 * Bounding rectangle that fully encloses the entire selection, even when it spans
	 * multiple lines or nodes. Equivalent to the union of all `innerRectangles`.
	 */
	outsideRectangle: DOMRect;
	/**
	 * Array of `DOMRect` objects, one per text line or range fragment.
	 * Use these for fine-grained positioning (e.g. rendering a floating toolbar aligned
	 * to each line of a multi-line selection).
	 */
	innerRectangles: DOMRect[];
}

Released under the MIT License.