useTextSelection

Track and access the currently selected text and its position on the page.

Select Text Below

React Hooks are functions that let you use state and other React features without writing a class. Highlighting text is a common interaction on the web, often used for sharing quotes or adding comments. Try selecting any part of this paragraph to see the Smart Highlighter in action.

Usage

import { useTextSelection } from 'usehooks-ts'
 
export default function Component() {
  const { text } = useTextSelection()
 
  return (
    <div>
      Selected text: {text}
    </div>
  )
}

API

const { text, clientRects } = useTextSelection()

Return Values

NameTypeDescription
textstringThe currently selected text string
clientRectsClientRect[]Array of DOMRect objects representing the selected text boundaries

Hook

import { useState, useLayoutEffect, useEffect } from 'react'
 
export interface ClientRect {
  x: number
  y: number
  top: number
  left: number
  bottom: number
  right: number
  width: number
  height: number
}
 
interface SelectionState {
  text: string
  clientRects: ClientRect[]
}
 
const defaultState: SelectionState = {
  text: '',
  clientRects: [],
}
 
const isBrowser = typeof window !== 'undefined'
const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect
 
export function useTextSelection(): SelectionState {
  const [state, setState] = useState<SelectionState>(defaultState)
 
  useIsomorphicLayoutEffect(() => {
    const handleSelectionChange = () => {
      const selection = window.getSelection()
 
      if (!selection || selection.rangeCount === 0) {
        setState(defaultState)
        return
      }
 
      const text = selection.toString()
      
      // If no text selected, clear state
      if (!text) {
        setState(defaultState)
        return
      }
 
      const range = selection.getRangeAt(0)
      const rects = range.getClientRects()
      const clientRects = Array.from(rects).map((rect) => ({
        x: rect.x,
        y: rect.y,
        top: rect.top,
        left: rect.left,
        bottom: rect.bottom,
        right: rect.right,
        width: rect.width,
        height: rect.height,
      }))
 
      setState({
        text,
        clientRects,
      })
    }
 
    document.addEventListener('selectionchange', handleSelectionChange)
 
    return () => {
      document.removeEventListener('selectionchange', handleSelectionChange)
    }
  }, [])
 
  return state
}