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 🙏

© 2026 – Pkg Stats / Ryan Hefner

hookify-react

v1.0.0

Published

A collection of optimized and reusable React hooks for state management, dom interaction, responsive design, storage, location, asynchronous management and performance improvements.

Readme

📦 hookify-react

🚀 A collection of high-performance, reusable, and production-ready React hooks to simplify state management, DOM interaction, location, async management and browser storage.

GitHub package.json version npm GitHub stars License GitHub issues

🌟 Table of Contents

🚀 Features

  • ✅ Fully typed with TypeScript
  • SSR-safe — works with Next.js, Remix and other server-rendered frameworks
  • ✅ No external dependencies (only react as a peer dependency)
  • ✅ Tree-shakeable ESM + CommonJS builds
  • ✅ Stable function identities (safe for dependency arrays and memoized children)
  • ✅ Automatic listener/timer cleanup — no leaks on unmount
  • ✅ Unit-tested with Vitest + Testing Library

📌 Installation

npm install hookify-react

or

yarn add hookify-react

Peer dependencies: react >= 18 and react-dom >= 18.

⚡ Quick Start

import { useRef } from "react";
import { useEventListener } from "hookify-react";

export default function Example() {
  const buttonRef = useRef<HTMLButtonElement>(null);

  useEventListener("click", () => alert("Button clicked!"), buttonRef);

  return <button ref={buttonRef}>Click Me</button>;
}

🔍 Available Hooks

| Category | Hooks | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Async Management | useDebounce, useInterval, useTimeout | | Effects | useAdvancedEffect, useUpdatedEffect | | DOM Interactions | useCopyToClipboard, useEventListener, useHover, useClickOutside, useOnlineStatus, useOnScreen, usePress, useScrollInfo | | Responsive Design | useSize, useWindowSize | | State Management | useArray, useCounter, useFormState, useHistory, usePrevious, useToggle | | Storage | useStorage, useLocalStorage, useSessionStorage | | Location | useGeoLocation |

📖 API Reference

Async Management

// Runs `callback` once the dependencies have been stable for `delay` ms.
useDebounce(callback: () => void, delay: number, deps: unknown[]): void

// Auto-starts on mount; pass `null` to pause. Returns a manual stopper.
useInterval(callback: () => void, interval?: number | null): { clear: () => void }

// Auto-starts on mount. Always uses the latest callback (no need to memoize).
useTimeout(callback: () => void, delay: number): {
  set: () => void;
  clear: () => void;
  reset: () => void;
}

Effects

// Like useEffect, but skips the very first render (runs only on updates).
useUpdatedEffect(effect: EffectCallback, deps: DependencyList): void

// Skips the first render and runs only when deps actually change (Object.is).
useAdvancedEffect(effect: EffectCallback, deps: DependencyList): void

DOM Interactions

// Uses the async Clipboard API with an execCommand fallback for insecure contexts.
useCopyToClipboard(resetDelay?: number): {
  copy: (text: string) => Promise<boolean>; // resolves to `true` on success
  isCopied: boolean;
  error: Error | null;
}

// Defaults to `window`; overloads also accept a Document or HTMLElement ref.
useEventListener(eventType, callback, elementRef?, options?): void

useHover<T extends HTMLElement>(): { ref: Ref<T>; isHovered: boolean }

useClickOutside<T extends HTMLElement>(
  callback: (event: MouseEvent | TouchEvent) => void,
): { ref: Ref<T> }

// Listens to both `online` and `offline`; SSR-safe (assumes online on the server).
useOnlineStatus(): { isOnline: boolean; onlineStatus: "online" | "offline" }

useOnScreen<T extends HTMLElement>(
  options?: string | { rootMargin?: string; threshold?: number | number[]; once?: boolean },
): { ref: Ref<T>; isVisible: boolean }

usePress<T extends HTMLElement>(): { ref: Ref<T>; isPressed: boolean }

useScrollInfo<T extends HTMLElement>(): {
  ref: Ref<T>;
  scrollX: number;
  scrollY: number;
  scrollDirection: "up" | "down" | "left" | "right" | "none";
  isScrolling: boolean;
  scrollProgress: number; // 0–100
}

Responsive Design

useSize<T extends HTMLElement>(): {
  ref: Ref<T>;
  size: { width; height; top; left; bottom; right } | null;
}

useWindowSize(): { width: number; height: number }

State Management

// `initialValue` defaults to []. All helper methods are referentially stable.
useArray<T>(initialValue?: T[]): [T[], SetState<T[]>, {
  push; pop; shift; unshift; removeByIndex; removeByValue;
  clear; replace; reset; filter; updateByIndex; updateByValue;
}]

// Optional { min, max } bounds clamp every update.
useCounter(initialValue?: number, options?: { min?: number; max?: number }): {
  count; increment; incrementByValue; decrement; decrementByValue; set; reset;
}

useFormState<T>(defaultValue, predicates, options?): [
  T,
  (value: T | ((prev: T) => T)) => void,
  { errors: string[]; isValid: boolean; status: "idle" | "valid" | "error" },
]

useHistory<T>(defaultValue, options?): [
  T,
  (value: T | ((prev: T) => T)) => void,
  { history: T[]; pointer: number; back; forward; go },
]

usePrevious<T>(value: T): T | null

// Call with no argument to flip, or a boolean to force a value.
useToggle(initialValue?: boolean): [boolean, (value?: boolean) => void]

Storage

All storage hooks are SSR-safe and fall back to the default value on the server. useLocalStorage additionally syncs across browser tabs via the storage event.

useLocalStorage<T>(key: string, defaultValue: T | (() => T)): [T, SetState<T>]
useSessionStorage<T>(key: string, defaultValue: T | (() => T)): [T, SetState<T>]
useStorage<T>(key: string, defaultValue: T | (() => T), type: "local" | "session"): [T, SetState<T>]

Location

// Options are read by value, so passing an inline object every render is safe.
useGeoLocation(options?: {
  enableHighAccuracy?: boolean;
  maximumAge?: number;
  timeout?: number;
  retryLimit?: number;
  retryDelay?: number;
}): { loading: boolean; error: { code: number; message: string } | null; coords: GeolocationCoordinates | null }

⚠️ Migration notes (since 0.0.5)

  • useCopyToClipboarderror is now an Error | null (previously a string). Render error.message. copy() now resolves to a boolean.
  • useOnlineStatus — now also exposes isOnline. The existing onlineStatus field is unchanged.
  • useStorage — the third argument is now "local" | "session" instead of a Storage object. useLocalStorage/useSessionStorage are unchanged.

🤝 Contributing

We welcome contributions! If you have suggestions for improvements or new hooks, please open an issue or submit a pull request.

npm install      # install dependencies
npm run test     # run the Vitest suite
npm run lint     # run ESLint
npm run build    # build the package
  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/AmazingFeature).
  3. Commit your changes (git commit -m 'Add some AmazingFeature').
  4. Push to the branch (git push origin feature/AmazingFeature).
  5. Open a pull request.

📜 License

This project is licensed under the MIT License — see the LICENSE file for details.

📬 Contact

For any inquiries or support, please reach out to [email protected].

Documentation

Visit hookify-react for full documentation.