← Back to all hooks

useEventListener

Type-safe event listeners with automatic cleanup. Prevents memory leaks and duplicate listeners in React components.

Live Demo

Interact with the page to see different event listeners in action.

🖱️ Mouse Position

X: 0
Y: 0

⌨️ Keyboard Events

Last Key Pressed

🌐 Window Clicks

0
Total clicks anywhere

📦 Element-Specific Clicks

0
Click this box!

📝 Event Log

No events yet

💡 Try This:

  • Move your mouse to see position tracking
  • Press any key on your keyboard
  • Click anywhere to increment window clicks
  • Click the purple box to see element-specific events

Code Examples

Window Events

import { useEventListener } from "@uiblock/hooks";
import { useState } from "react";

export default function MouseTracker() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEventListener('mousemove', (e) => {
    setPosition({ x: e.clientX, y: e.clientY });
  });

  return <div>Mouse: {position.x}, {position.y}</div>;
}

Element-Specific Events

import { useEventListener } from "@uiblock/hooks";
import { useRef, useState } from "react";

export default function ClickableBox() {
  const [clicks, setClicks] = useState(0);
  const boxRef = useRef<HTMLDivElement>(null);

  useEventListener('click', () => {
    setClicks(prev => prev + 1);
  }, boxRef);

  return (
    <div ref={boxRef}>
      Clicked {clicks} times
    </div>
  );
}

Keyboard Shortcuts

import { useEventListener } from "@uiblock/hooks";

export default function KeyboardShortcuts() {
  useEventListener('keydown', (e) => {
    if (e.metaKey && e.key === 's') {
      e.preventDefault();
      console.log('Save shortcut triggered');
    }
    if (e.metaKey && e.key === 'k') {
      e.preventDefault();
      console.log('Search shortcut triggered');
    }
  });

  return <div>Press Cmd+S or Cmd+K</div>;
}

Scroll Events

import { useEventListener } from "@uiblock/hooks";
import { useState } from "react";

export default function ScrollIndicator() {
  const [scrollY, setScrollY] = useState(0);

  useEventListener('scroll', () => {
    setScrollY(window.scrollY);
  });

  const scrollPercentage = (scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;

  return (
    <div style={{ width: `${scrollPercentage}%`, height: 4, background: 'blue' }} />
  );
}

How It Works

Here's the implementation:

import { useEffect, useRef } from 'react'

export function useEventListener<K extends keyof WindowEventMap>(
  eventName: K,
  handler: (event: WindowEventMap[K]) => void,
  element?: React.RefObject<HTMLElement>,
  options?: boolean | AddEventListenerOptions
) {
  const savedHandler = useRef(handler)

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

  useEffect(() => {
    const targetElement = element?.current ?? window
    
    if (!(targetElement && targetElement.addEventListener)) return

    const eventListener = (event: Event) => {
      savedHandler.current(event as WindowEventMap[K])
    }

    targetElement.addEventListener(eventName, eventListener, options)

    return () => {
      targetElement.removeEventListener(eventName, eventListener, options)
    }
  }, [eventName, element, options])
}

Key Features:

  • Automatic cleanup on unmount
  • Type-safe event handlers
  • Supports window, document, and element events
  • Prevents memory leaks

API Reference

Parameters

eventName: string

Name of the event to listen for

handler: (event) => void

Event handler function

element?: RefObject

Optional element ref (defaults to window)

options?: AddEventListenerOptions

Optional event listener options

💡 Use Cases

  • Mouse and keyboard event tracking
  • Scroll position monitoring
  • Window resize handlers
  • Keyboard shortcuts
  • Click outside detection
  • Custom element interactions