useEventListener


Custom hook that attaches event listeners to DOM elements, the window, or media query lists.

Usage

import { useRef } from 'react'

import { useEventListener } from 'usehooks-ts'

export default function Component() {
  // Define button ref
  const buttonRef = useRef<HTMLButtonElement>(null)
  const documentRef = useRef<Document>(document)

  const onScroll = (event: Event) => {
    console.log('window scrolled!', event)
  }

  const onClick = (event: Event) => {
    console.log('button clicked!', event)
  }

  const onVisibilityChange = (event: Event) => {
    console.log('doc visibility changed!', {
      isVisible: !document.hidden,
      event,
    })
  }

  // example with window based event
  useEventListener('scroll', onScroll)

  // example with document based event
  useEventListener('visibilitychange', onVisibilityChange, documentRef)

  // example with element based event
  useEventListener('click', onClick, buttonRef)

  return (
    <div style={{ minHeight: '200vh' }}>
      <button ref={buttonRef}>Click me</button>
    </div>
  )
}

API

useEventListener<K>(eventName, handler, element, options?): void

Custom hook that attaches event listeners to DOM elements, the window, or media query lists.

Type parameters

NameType
Kextends "change"

Parameters

NameTypeDescription
eventNameKThe name of the event to listen for.
handler(event: MediaQueryListEventMap[K]) => voidThe event handler function.
elementRefObject<MediaQueryList>The DOM element or media query list to attach the event listener to (optional).
options?boolean | AddEventListenerOptionsAn options object that specifies characteristics about the event listener (optional).

Returns

void

useEventListener<K>(eventName, handler, element?, options?): void

Custom hook that attaches event listeners to DOM elements, the window, or media query lists.

Type parameters

NameType
Kextends keyof WindowEventMap

Parameters

NameTypeDescription
eventNameKThe name of the event to listen for.
handler(event: WindowEventMap[K]) => voidThe event handler function.
element?undefinedThe DOM element or media query list to attach the event listener to (optional).
options?boolean | AddEventListenerOptionsAn options object that specifies characteristics about the event listener (optional).

Returns

void

useEventListener<K, T>(eventName, handler, element, options?): void

Custom hook that attaches event listeners to DOM elements, the window, or media query lists.

Type parameters

NameTypeDescription
Kextends "copy" | "change" | "test-event" | "abort" | "animationcancel" | "animationend" | "animationiteration" | "animationstart" | "auxclick" | "beforeinput" | "blur" | "cancel" | "canplay" | "canplaythrough" | "click" | "close" | "compositionend" | "compositionstart" | "compositionupdate" | "contextmenu" | "cuechange" | "cut" | "dblclick" | "drag" | "dragend" | "dragenter" | "dragleave" | "dragover" | "dragstart" | "drop" | "durationchange" | "emptied" | "ended" | "error" | "focus" | "focusin" | "focusout" | "formdata" | "gotpointercapture" | "input" | "invalid" | "keydown" | "keypress" | "keyup" | "load" | "loadeddata" | "loadedmetadata" | "loadstart" | "lostpointercapture" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" | "paste" | "pause" | "play" | "playing" | "pointercancel" | "pointerdown" | "pointerenter" | "pointerleave" | "pointermove" | "pointerout" | "pointerover" | "pointerup" | "progress" | "ratechange" | "reset" | "resize" | "scroll" | "scrollend" | "securitypolicyviolation" | "seeked" | "seeking" | "select" | "selectionchange" | "selectstart" | "slotchange" | "stalled" | "submit" | "suspend" | "timeupdate" | "toggle" | "touchcancel" | "touchend" | "touchmove" | "touchstart" | "transitioncancel" | "transitionend" | "transitionrun" | "transitionstart" | "volumechange" | "waiting" | "webkitanimationend" | "webkitanimationiteration" | "webkitanimationstart" | "webkittransitionend" | "wheel" | "fullscreenchange" | "fullscreenerror"-
Textends Element = K extends keyof HTMLElementEventMap ? HTMLDivElement : SVGElementThe type of the DOM element (default is HTMLElement).

Parameters

NameTypeDescription
eventNameKThe name of the event to listen for.
handler(event: HTMLElementEventMap[K]) => void | (event: SVGElementEventMap[K]) => voidThe event handler function.
elementRefObject<T>The DOM element or media query list to attach the event listener to (optional).
options?boolean | AddEventListenerOptionsAn options object that specifies characteristics about the event listener (optional).

Returns

void

useEventListener<K>(eventName, handler, element, options?): void

Custom hook that attaches event listeners to DOM elements, the window, or media query lists.

Type parameters

NameType
Kextends keyof DocumentEventMap

Parameters

NameTypeDescription
eventNameKThe name of the event to listen for.
handler(event: DocumentEventMap[K]) => voidThe event handler function.
elementRefObject<Document>The DOM element or media query list to attach the event listener to (optional).
options?boolean | AddEventListenerOptionsAn options object that specifies characteristics about the event listener (optional).

Returns

void

Hook

import { useEffect, useRef } from 'react'

import type { RefObject } from 'react'

import { useIsomorphicLayoutEffect } from 'usehooks-ts'

// MediaQueryList Event based useEventListener interface
function useEventListener<K extends keyof MediaQueryListEventMap>(
  eventName: K,
  handler: (event: MediaQueryListEventMap[K]) => void,
  element: RefObject<MediaQueryList>,
  options?: boolean | AddEventListenerOptions,
): void

// Window Event based useEventListener interface
function useEventListener<K extends keyof WindowEventMap>(
  eventName: K,
  handler: (event: WindowEventMap[K]) => void,
  element?: undefined,
  options?: boolean | AddEventListenerOptions,
): void

// Element Event based useEventListener interface
function useEventListener<
  K extends keyof HTMLElementEventMap & keyof SVGElementEventMap,
  T extends Element = K extends keyof HTMLElementEventMap
    ? HTMLDivElement
    : SVGElement,
>(
  eventName: K,
  handler:
    | ((event: HTMLElementEventMap[K]) => void)
    | ((event: SVGElementEventMap[K]) => void),
  element: RefObject<T>,
  options?: boolean | AddEventListenerOptions,
): void

// Document Event based useEventListener interface
function useEventListener<K extends keyof DocumentEventMap>(
  eventName: K,
  handler: (event: DocumentEventMap[K]) => void,
  element: RefObject<Document>,
  options?: boolean | AddEventListenerOptions,
): void

function useEventListener<
  KW extends keyof WindowEventMap,
  KH extends keyof HTMLElementEventMap & keyof SVGElementEventMap,
  KM extends keyof MediaQueryListEventMap,
  T extends HTMLElement | SVGAElement | MediaQueryList = HTMLElement,
>(
  eventName: KW | KH | KM,
  handler: (
    event:
      | WindowEventMap[KW]
      | HTMLElementEventMap[KH]
      | SVGElementEventMap[KH]
      | MediaQueryListEventMap[KM]
      | Event,
  ) => void,
  element?: RefObject<T>,
  options?: boolean | AddEventListenerOptions,
) {
  // Create a ref that stores handler
  const savedHandler = useRef(handler)

  useIsomorphicLayoutEffect(() => {
    savedHandler.current = handler
  }, [handler])

  useEffect(() => {
    // Define the listening target
    const targetElement: T | Window = element?.current ?? window

    if (!(targetElement && targetElement.addEventListener)) return

    // Create event listener that calls handler function stored in ref
    const listener: typeof handler = event => {
      savedHandler.current(event)
    }

    targetElement.addEventListener(eventName, listener, options)

    // Remove event listener on cleanup
    return () => {
      targetElement.removeEventListener(eventName, listener, options)
    }
  }, [eventName, element, options])
}

export { useEventListener }