npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@aniruddha1806/use-event-listener

v1.0.3

Published

A flexible, type-safe React hook for managing event listeners with automatic cleanup

Readme

React useEventListener Hook

A type-safe, versatile event listener hook for React applications with TypeScript support. Easily attach event listeners to any DOM element, window, or document with proper cleanup and type safety.

Installation

npm install @aniruddha1806/use-event-listener

Features

  • 🎯 TypeScript support with full type definitions for all event types
  • 🌐 Works with Window, Document, and any HTML Element
  • 🔄 Supports both direct elements and React refs
  • 🧹 Automatic cleanup on unmount to prevent memory leaks
  • ⚠️ Development mode warnings for common mistakes
  • 🛡️ Type-safe event handling with proper event types
  • 📦 Tiny footprint with zero dependencies
  • 🔍 Preserves the original listener reference between renders

Quick Start

import { useRef } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function App() {
  // Listen for window events
  useEventListener(window, 'resize', () => {
    console.log('Window resized!');
  });
  
  // Listen for document events
  useEventListener(document, 'visibilitychange', () => {
    console.log('Tab visibility changed!');
  });
  
  // Listen for element events using a ref
  const buttonRef = useRef(null);
  useEventListener(buttonRef, 'click', () => {
    console.log('Button clicked!');
  });
  
  return (
    <div>
      <h1>useEventListener Example</h1>
      <button ref={buttonRef}>Click me</button>
    </div>
  );
}

API

Parameters

| Parameter | Type | Description | |-----------|------|-------------| | target | Window \| Document \| HTMLElement \| RefObject<HTMLElement> \| Element \| RefObject<Element> | The event target | | type | string | The event type (e.g., 'click', 'resize') | | listener | (event: Event) => void | The event callback function | | options | boolean \| AddEventListenerOptions | Optional event listener options |

Returns

void - This hook doesn't return anything.

Examples

Window Event Listeners

Listen for global window events:

import useEventListener from '@aniruddha1806/use-event-listener';

function WindowEvents() {
  // Track window size
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });
  
  // Update dimensions on window resize
  useEventListener(window, 'resize', () => {
    setWindowSize({
      width: window.innerWidth,
      height: window.innerHeight,
    });
  });
  
  // Track online status
  const [isOnline, setIsOnline] = useState(navigator.onLine);
  
  useEventListener(window, 'online', () => setIsOnline(true));
  useEventListener(window, 'offline', () => setIsOnline(false));
  
  return (
    <div>
      <h2>Window Size</h2>
      <p>Width: {windowSize.width}px</p>
      <p>Height: {windowSize.height}px</p>
      
      <h2>Network Status</h2>
      <p>You are {isOnline ? 'online' : 'offline'}</p>
    </div>
  );
}

Document Event Listeners

Listen for document-level events:

import { useState } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function KeyboardShortcuts() {
  const [lastKey, setLastKey] = useState('');
  const [isFullScreen, setIsFullScreen] = useState(false);
  
  // Track keyboard shortcuts
  useEventListener(document, 'keydown', (event) => {
    setLastKey(event.key);
    
    // Handle keyboard shortcuts
    if (event.key === 'F' && event.ctrlKey) {
      event.preventDefault();
      toggleFullScreen();
    }
  });
  
  // Track document visibility
  useEventListener(document, 'visibilitychange', () => {
    console.log('Document visibility:', document.visibilityState);
  });
  
  const toggleFullScreen = () => {
    if (!document.fullscreenElement) {
      document.documentElement.requestFullscreen();
      setIsFullScreen(true);
    } else {
      document.exitFullscreen();
      setIsFullScreen(false);
    }
  };
  
  return (
    <div>
      <h2>Keyboard Shortcuts</h2>
      <p>Last key pressed: {lastKey || 'None'}</p>
      <p>Press Ctrl+F to toggle fullscreen</p>
      <p>Fullscreen: {isFullScreen ? 'Yes' : 'No'}</p>
    </div>
  );
}

Element Event Listeners with Refs

Listen for events on specific DOM elements using refs:

import { useRef, useState } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function DragAndDrop() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [isDragging, setIsDragging] = useState(false);
  const elementRef = useRef(null);
  
  // Handle mouse down on the element
  useEventListener(elementRef, 'mousedown', (event) => {
    setIsDragging(true);
    // Prevent text selection during drag
    event.preventDefault();
  });
  
  // Handle mouse move for dragging
  useEventListener(document, 'mousemove', (event) => {
    if (isDragging) {
      setPosition({
        x: event.clientX - 50, // Adjust for element size
        y: event.clientY - 50,
      });
    }
  });
  
  // Handle mouse up to stop dragging
  useEventListener(document, 'mouseup', () => {
    setIsDragging(false);
  });
  
  return (
    <div>
      <h2>Drag and Drop</h2>
      <div
        ref={elementRef}
        style={{
          position: 'absolute',
          left: `${position.x}px`,
          top: `${position.y}px`,
          width: '100px',
          height: '100px',
          backgroundColor: isDragging ? 'lightcoral' : 'lightblue',
          cursor: 'grab',
          userSelect: 'none',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          borderRadius: '8px',
        }}
      >
        Drag me
      </div>
    </div>
  );
}

Form Input Events

Handle form input events:

import { useRef, useState } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function FormEvents() {
  const [inputValue, setInputValue] = useState('');
  const [isFocused, setIsFocused] = useState(false);
  const inputRef = useRef(null);
  
  // Handle input changes
  useEventListener(inputRef, 'input', (event) => {
    setInputValue(event.target.value);
  });
  
  // Handle focus events
  useEventListener(inputRef, 'focus', () => {
    setIsFocused(true);
  });
  
  // Handle blur events
  useEventListener(inputRef, 'blur', () => {
    setIsFocused(false);
  });
  
  return (
    <div>
      <h2>Form Input Events</h2>
      <input
        ref={inputRef}
        type="text"
        placeholder="Type something..."
        style={{
          padding: '8px',
          border: `2px solid ${isFocused ? 'blue' : 'gray'}`,
          borderRadius: '4px',
          outline: 'none',
        }}
      />
      <p>Current value: {inputValue}</p>
      <p>Input is {isFocused ? 'focused' : 'not focused'}</p>
    </div>
  );
}

Passive Event Listeners

Use passive event listeners for better scroll performance:

import { useRef } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function SmoothScroll() {
  const scrollContainerRef = useRef(null);
  
  // Use passive: true for better scroll performance
  useEventListener(
    scrollContainerRef,
    'scroll',
    (event) => {
      console.log('Scroll position:', event.target.scrollTop);
    },
    { passive: true }
  );
  
  return (
    <div
      ref={scrollContainerRef}
      style={{
        height: '300px',
        overflow: 'auto',
        border: '1px solid #ccc',
        padding: '16px',
      }}
    >
      <h2>Smooth Scrolling</h2>
      {Array.from({ length: 50 }).map((_, index) => (
        <p key={index}>Scroll content item {index + 1}</p>
      ))}
    </div>
  );
}

TypeScript Usage

The hook provides full TypeScript support with proper type inference:

import { useRef, useState } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

interface MousePosition {
  x: number;
  y: number;
}

function TypedEventExample() {
  // Properly typed state
  const [mousePosition, setMousePosition] = useState<MousePosition>({ x: 0, y: 0 });
  const [clicks, setClicks] = useState<number>(0);
  
  // Ref with proper HTML element type
  const buttonRef = useRef<HTMLButtonElement>(null);
  
  // Window event with proper event type
  useEventListener(window, 'mousemove', (event: MouseEvent) => {
    setMousePosition({ x: event.clientX, y: event.clientY });
  });
  
  // Element event with proper event type
  useEventListener(buttonRef, 'click', (event: MouseEvent) => {
    // event is properly typed as MouseEvent
    setClicks(prev => prev + 1);
    console.log('Click position:', event.clientX, event.clientY);
  });
  
  // Document event with proper event type
  useEventListener(document, 'visibilitychange', (event: Event) => {
    console.log('Visibility changed:', document.visibilityState);
  });
  
  return (
    <div>
      <h2>TypeScript Event Handling</h2>
      <p>
        Mouse position: {mousePosition.x}, {mousePosition.y}
      </p>
      <button ref={buttonRef}>
        Click me ({clicks} clicks)
      </button>
    </div>
  );
}

Advanced Usage

Event Delegation

Implement event delegation pattern:

import { useRef } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function EventDelegation() {
  const listRef = useRef(null);
  
  // Handle clicks on any list item through event delegation
  useEventListener(listRef, 'click', (event) => {
    // Find the closest list item that was clicked
    const listItem = event.target.closest('li');
    if (listItem) {
      console.log('List item clicked:', listItem.dataset.id);
      // Highlight the clicked item
      listItem.style.backgroundColor = '#f0f0f0';
      setTimeout(() => {
        listItem.style.backgroundColor = '';
      }, 300);
    }
  });
  
  return (
    <div>
      <h2>Event Delegation</h2>
      <ul ref={listRef} style={{ listStyle: 'none', padding: 0 }}>
        {Array.from({ length: 10 }).map((_, index) => (
          <li
            key={index}
            data-id={index + 1}
            style={{
              padding: '8px 16px',
              margin: '4px 0',
              border: '1px solid #ddd',
              borderRadius: '4px',
              cursor: 'pointer',
              transition: 'background-color 0.2s',
            }}
          >
            Item {index + 1}
          </li>
        ))}
      </ul>
    </div>
  );
}

Conditional Event Listeners

Add or remove event listeners conditionally:

import { useState } from 'react';
import useEventListener from '@aniruddha1806/use-event-listener';

function ConditionalEvents() {
  const [isListening, setIsListening] = useState(false);
  const [events, setEvents] = useState([]);
  
  // Only add the event listener when isListening is true
  useEventListener(
    isListening ? window : null,
    'mousemove',
    (event) => {
      setEvents((prev) => [
        ...prev.slice(-4),
        { x: event.clientX, y: event.clientY, time: new Date().toLocaleTimeString() },
      ]);
    },
    { passive: true }
  );
  
  return (
    <div>
      <h2>Conditional Event Listener</h2>
      <button onClick={() => setIsListening(!isListening)}>
        {isListening ? 'Stop Listening' : 'Start Listening'}
      </button>
      <p>Status: {isListening ? 'Tracking mouse movement' : 'Not tracking'}</p>
      
      <h3>Recent Mouse Events:</h3>
      <ul>
        {events.map((event, index) => (
          <li key={index}>
            Position: ({event.x}, {event.y}) at {event.time}
          </li>
        ))}
      </ul>
    </div>
  );
}