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.