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

iso-hooks

v1.3.5

Published

A collection of modern, SSR-safe React hooks.

Readme

📦 Iso Hooks

A modern, lightweight, and fully SSR-safe collection of handy React hooks — type-safe, dependency-free, and made for today's frameworks like Next.js, Remix, and Vite.


✨ Features

  • 🛡️ SSR-Safe: Works seamlessly with server-side rendering.
  • 🔷 TypeScript First: Strong types and dedicated definitions included.
  • 🌲 Tree-shakeable: Import only what you need.
  • 🪶 No Dependencies: Truly lightweight bundle.

🚀 Installation

npm install iso-hooks
# or
yarn add iso-hooks
# or
pnpm add iso-hooks

📚 Documentation

Iso Hooks provides all the essentials for modern React development with a simple and secure API.


1️⃣ useLocalStorage

Persist and retrieve values from localStorage in an SSR-friendly way, automatically syncing across tabs.

import { useLocalStorage } from "iso-hooks";

const App = () => {
  const [theme, setTheme] = useLocalStorage("theme", "light");

  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      Current theme: {theme}
    </button>
  );
};

2️⃣ useOnClickOutside

Detect clicks outside any element — great for closing popups, modals, or menus.

import { useRef, useState } from "react";
import { useOnClickOutside } from "iso-hooks";

const Modal = () => {
  const [isOpen, setOpen] = useState(true);
  const ref = useRef<HTMLDivElement>(null);

  useOnClickOutside(ref, () => setOpen(false));

  if (!isOpen) return null;

  return (
    <div className="overlay">
      <div ref={ref} className="modal-content">
        Click outside to close me!
      </div>
    </div>
  );
};

3️⃣ useWindowSize

Access live window size — SSR-safe, preventing hydration errors.

import { useWindowSize } from "iso-hooks";

const MyComponent = () => {
  const { width, height } = useWindowSize();

  if (!width) return <div>Loading...</div>;

  return (
    <div>
      Window size: {width} x {height}
    </div>
  );
};

4️⃣ useDebounce

Debounce values or functions — ideal for search boxes or rapid input.

import { useState, useEffect } from "react";
import { useDebounce } from "iso-hooks";

const SearchInput = () => {
  const [searchTerm, setSearchTerm] = useState("");
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  useEffect(() => {
    if (debouncedSearchTerm) {
      console.log("Searching for:", debouncedSearchTerm);
    }
  }, [debouncedSearchTerm]);

  return (
    <input
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="Search..."
    />
  );
};

5️⃣ useInterval

A declarative, React-safe interval hook — no more stale closure bugs.

import { useState } from "react";
import { useInterval } from "iso-hooks";

const Timer = () => {
  const [count, setCount] = useState(0);
  const [isPlaying, setPlaying] = useState(true);

  useInterval(() => setCount((c) => c + 1), isPlaying ? 1000 : null);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setPlaying(!isPlaying)}>
        {isPlaying ? "Pause" : "Resume"}
      </button>
    </div>
  );
};

6️⃣ useScrollLock

Lock the body scroll — perfect for modals, mobile menus, or drawers.

import { useScrollLock } from "iso-hooks";

const Modal = () => {
  const { lock, unlock } = useScrollLock();

  return (
    <div>
      <button onClick={lock}>Lock Scroll</button>
      <button onClick={unlock}>Unlock Scroll</button>
    </div>
  );
};

7️⃣ useCopyToClipboard

Copy any text to the user's clipboard easily.

import { useCopyToClipboard } from "iso-hooks";

const CopyButton = () => {
  const [copiedText, copy] = useCopyToClipboard();

  return (
    <button onClick={() => copy("Hello World!")}>
      {copiedText ? "Copied!" : "Copy Text"}
    </button>
  );
};

8️⃣ useMediaQuery

React to CSS media queries in JS (SSR returns false until client hydration).

import { useMediaQuery } from "iso-hooks";

const ResponsiveComponent = () => {
  const isMobile = useMediaQuery("(max-width: 768px)");

  return <div>{isMobile ? <p>Mobile Layout</p> : <p>Desktop Layout</p>}</div>;
};

9️⃣ useEventListener

Add event listeners safely and declaratively to any element or the window.

import { useEventListener } from "iso-hooks";

const LogKeydown = () => {
  useEventListener("keydown", (e: KeyboardEvent) => {
    console.log("Key pressed:", e.key);
  });

  return <div>Press any key and check the console.</div>;
};

🔟 useHover

Detect when an element is hovered.

import { useHover } from "iso-hooks";

const HoverComponent = () => {
  const [ref, isHovered] = useHover<HTMLDivElement>();

  return (
    <div ref={ref}>{isHovered ? "You're hovering me!" : "Hover over me"}</div>
  );
};

11️⃣ useTimeout

Set timeouts in React, safely cleared on unmount or updates.

import { useState } from "react";
import { useTimeout } from "iso-hooks";

const TimeoutDemo = () => {
  const [message, setMessage] = useState("Waiting...");
  useTimeout(() => setMessage("Timeout finished!"), 2000);

  return <div>{message}</div>;
};

12️⃣ useIntersectionObserver

React to elements entering or leaving the viewport.

import { useRef } from "react";
import { useIntersectionObserver } from "iso-hooks";

const LazyComponent = () => {
  const ref = useRef<HTMLDivElement>(null);
  const entry = useIntersectionObserver(ref);

  return (
    <div ref={ref}>
      {entry?.isIntersecting ? "Visible in viewport" : "Scroll to reveal me!"}
    </div>
  );
};

13️⃣ useMeasure

Measure the size and position of a DOM element, SSR-safe.

import { useRef } from "react";
import { useMeasure } from "iso-hooks";

const BoxMeasurement = () => {
  const ref = useRef<HTMLDivElement>(null);
  const bounds = useMeasure(ref);

  return (
    <div>
      <div
        ref={ref}
        style={{ width: 200, height: 150, background: "#eee", margin: 16 }}
      >
        Resize me!
      </div>
      <pre>{JSON.stringify(bounds, null, 2)}</pre>
    </div>
  );
};

14️⃣ useFetch

Easy HTTP requests, with auto-cancellation, reactivity, and loading/error state.

import { useFetch } from "iso-hooks";

const MyApiData = () => {
  // Pass [] as initial data so 'data' is never null!
  const { data, loading, error, refetch } = useFetch<Todo[]>(
    "https://jsonplaceholder.typicode.com/todos",
    { initialData: [] }
  );

  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {loading && <p>Refreshing...</p>}
      <ul>
        {data.map((todo) => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>
      <button onClick={refetch}>Refetch</button>
    </div>
  );
};

15️⃣ useThrottleCallback

Throttle a callback — fires at the start of the interval and, optionally, once at the end.

import { useThrottleCallback } from "iso-hooks";

const DemoThrottle = () => {
  const throttledLog = useThrottleCallback(() => {
    console.log("Throttled!");
  }, 1000);

  return <button onClick={throttledLog}>Throttle me</button>;
};

16️⃣ usePrevious

Get the previous value of any variable or state — perfect for comparisons and animations.

import { usePrevious } from "iso-hooks";

const Counter = () => {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);

  return (
    <div>
      <div>Current: {count}</div>
      <div>Previous: {prevCount}</div>
      <button onClick={() => setCount((c) => c + 1)}>Increment</button>
    </div>
  );
};

17️⃣ useKeyPress

Detect when a specific keyboard key is pressed.

import { useKeyPress } from "iso-hooks";

const SpaceLogger = () => {
  const spacePressed = useKeyPress(" ");

  return <div>{spacePressed ? "Space key is down" : "Space key is up"}</div>;
};

18️⃣ useToggle

Easy boolean state with a toggle function.

import { useToggle } from "iso-hooks";

const ToggleDemo = () => {
  const [on, toggle] = useToggle(false);

  return <button onClick={toggle}>{on ? "On" : "Off"}</button>;
};

19️⃣ useUpdateEffect

Fire an effect only when dependencies change (not on mount) — a safe replacement for regular useEffect after mount.

import { useState } from "react";
import { useUpdateEffect } from "iso-hooks";

const OnlyUpdates = () => {
  const [val, setVal] = useState(0);

  useUpdateEffect(() => {
    alert("State changed: " + val);
  }, [val]);

  return <button onClick={() => setVal((v) => v + 1)}>+1</button>;
};

2️⃣0️⃣ useIsomorphicLayoutEffect

Like useLayoutEffect, but SSR-safe: automatically falls back to useEffect on the server.

import { useIsomorphicLayoutEffect } from "iso-hooks";

const UseIsoLayoutDemo = () => {
  useIsomorphicLayoutEffect(() => {
    // Runs only on client, not on server
    // Useful for DOM measurements
  }, []);

  return <div>Check the console (client only)</div>;
};

2️⃣2️⃣ useMousePosition

Get the mouse position within the viewport, updated live.

import { useMousePosition } from "iso-hooks";

const MousePositionDemo = () => {
  const { x, y } = useMousePosition();

  return (
    <div>
      Mouse: {x}, {y}
    </div>
  );
};

🗂 Full Hooks List

| Hook Name | Description | | ------------------------- | -------------------------------------------- | | useCopyToClipboard | Copy text to the clipboard | | useDebounce | Debounce values or functions | | useDocumentTitle | Update the document title (SSR-safe) | | useEventListener | Attach any event listener | | useFetch | Simple & advanced HTTP data fetching | | useFocusTrap | Trap keyboard focus within an element | | useHover | Detect when element is hovered | | useIntersectionObserver | Observe element visibility in viewport | | useInterval | Safe and modern setInterval | | useIsomorphicLayoutEffect | SSR-safe version of useLayoutEffect | | useKeyPress | Listen to specific keyboard key press | | useLocalStorage | Save and retrieve values from localStorage | | useMediaQuery | Listen to media queries in JS | | useMeasure | Measure element size and position (SSR-safe) | | useMousePosition | Track mouse position in the viewport | | useNetworkStatus | Listen to online/offline status | | useOnClickOutside | Detect clicks outside elements | | usePrevious | Save previous value of a variable | | useScrollLock | Lock or unlock scrolling | | useThrottleCallback | Throttle callbacks | | useTimeout | Safe setTimeout in React | | useToggle | Manage a boolean toggle | | useUpdateEffect | Run effects after initial mount only | | useWindowSize | Get live browser window size |


🤝 Contributing

All contributions, issues, and suggestions are welcome! Please open a Pull Request or Issue.


📄 License

MIT © ManiAlipour