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

@gmana/react-hooks

v0.0.17

Published

React hooks

Readme

@gmana/react-hooks

npm version TypeScript MIT License

A comprehensive collection of production-ready React hooks for modern applications. Built with TypeScript and fully tested.

✨ Features

  • 🚀 80+ Production-Ready Hooks - Covering state management, UI interactions, network requests, and more
  • 📦 TypeScript Support - Full type safety with comprehensive TypeScript definitions
  • 🧪 Thoroughly Tested - Complete test coverage with Vitest
  • 📱 Mobile-First - Responsive design helpers and mobile-optimized hooks
  • 🎯 Tree-Shakable - Import only what you need
  • ⚡ Next.js Ready - Full SSR support and hydration safety for Next.js applications
  • 🔄 Server-Side Rendering - SSR compatible with Next.js and other frameworks
  • 📖 Well Documented - Comprehensive examples and API documentation

📦 Installation

# npm
npm install @gmana/react-hooks

# yarn
yarn add @gmana/react-hooks

# pnpm
pnpm add @gmana/react-hooks

# bun
bun add @gmana/react-hooks

🚀 Quick Start

import React from "react"
import { useBoolean, useCounter, useFetch } from "@gmana/react-hooks"

function App() {
  const { value: isVisible, toggle } = useBoolean(false)
  const { count, increment, decrement } = useCounter(0)
  const { data, error } = useFetch<{ title: string }>("https://api.example.com/data")

  if (error) return <div>Error loading data</div>
  if (!data) return <div>Loading...</div>

  return (
    <div>
      <h1>{data.title}</h1>
      <p>Count: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>

      <button onClick={toggle}>{isVisible ? "Hide" : "Show"} Content</button>
      {isVisible && <div>Toggle content</div>}
    </div>
  )
}

📚 Available Hooks

🔄 State Management

  • useBoolean — Boolean state with setTrue / setFalse / toggle
  • useToggle — Boolean state as a tuple, where the setter doubles as a toggle
  • useToggleState — Boolean state destructurable as either an array or an object
  • useCounter — Numeric counter with min / max clamping and a custom step
  • useArray — Array state with immutable push / insertAt / update / remove helpers
  • useMap — Map state with immutable actions and no-op bail-outs
  • useSet — Set state for selection, filters, and expansion state
  • useQueue — FIFO queue for toasts, tours, and retry backlogs
  • useObjectState — Object state updated by shallow merge instead of spreading
  • useStateHistory — State with undo / redo and a capped history
  • useControllableState — One hook for both controlled and uncontrolled components
  • useStep — 1-based step index for wizards, clamped rather than throwing
  • useRafState — State whose updates are coalesced to one per animation frame

🎨 UI & Layout

  • useResizeObserver — Measure an element via ResizeObserver
  • useElementSize — Measure an element through a callback ref, no useRef plumbing
  • useDimensions — Border-box dimensions of a referenced element
  • useWindowSize — Viewport dimensions, with optional resize debouncing
  • useWindowScroll — Scroll position, direction, and scroll helpers
  • useScroll — Whether the page is scrolled past a threshold
  • useHover — Whether the pointer is over an element
  • useInView — Whether an element is on screen
  • useIntersectionObserver — Full IntersectionObserver access, with freezeOnceVisible
  • useInfiniteScroll — Load the next page when a sentinel scrolls into view
  • useOnClickOutside — Detect clicks outside one element or several
  • useClickAnyWhere — Detect any click in the document
  • useLockedBody — Lock body scroll, with scrollbar compensation and nested locks
  • useFocusTrap — Confine keyboard focus to a container (modals, drawers)
  • useFullscreen — Present an element fullscreen
  • useSidebar — Open/closed state shaped for Radix-style components
  • useImageOnLoad — Cross-fade a blurred thumbnail into a full-size image
  • useTextSelection — The current selection and its bounding rect
  • useMutationObserver — React to DOM changes made outside React

🌐 Network & Data

  • useFetch — JSON requests with caching, abort, skip, and refetch
  • useAsync — Run any async function on demand and track its lifecycle
  • useAsyncEffect — Async effects with cancellation and an AbortSignal
  • useNetworkInformation — Connection quality and online status
  • useOfflineDetector / useOnlineStatus — Online/offline status
  • useBroadcastChannel — Send messages between tabs on the same origin

💾 Storage & Persistence

  • useLocalStorage — localStorage state, synced across hooks and tabs
  • useSessionStorage — The same, scoped to the tab
  • useReadLocalStorage — Read a key you don't own, staying in sync
  • useCookie — Read and write a cookie as state
  • setCookie / getCookie / removeCookie — Standalone cookie helpers
  • useEstimateStorage — Storage usage and remaining quota

📱 Device & Browser APIs

  • useMediaQuery — CSS media query matching
  • useIsMobile — Whether the viewport is below a mobile breakpoint
  • useScreen — Screen metrics, updated on resize and rotation
  • useOrientation — Portrait/landscape and rotation angle
  • useGeolocation — Device position, with watch and gesture-friendly request
  • usePermission — Observe a browser permission without prompting
  • useCopyToClipboard — Copy text, with a transient "copied!" state
  • useClipboard — Deprecated alias for useCopyToClipboard
  • useScript — Load an external script and track its status
  • useDocumentTitle — Set document.title while mounted
  • useFavicon — Swap the page favicon while mounted
  • useDocumentVisibility / useIsDocumentVisible — Whether the tab is visible
  • usePreferredLanguage / usePreferredLanguages — The user's language preferences
  • usePrefersReducedMotion — Whether the user asked to reduce motion

⌨️ Input & Interaction

  • useKeyPress — Keyboard shortcuts, including cross-platform "mod+k"
  • useLongPress — Press-and-hold gestures across mouse, touch, and pen
  • useIdle — Whether the user has been inactive
  • usePageLeave — Exit-intent detection
  • useUnloadWarning — Warn before leaving with unsaved changes

⏱️ Timing & Effects

  • useDebounce — Debounce a value, with flush / cancel / maxWait
  • useDebouncedCallback — Debounce a callback, with a stable identity
  • useThrottle — Throttle a callback, with leading/trailing control
  • useTimeout — Declarative setTimeout with manual controls
  • useInterval — Declarative setInterval with a pausable delay
  • useCountdown — Drift-free countdown with pause and resume

🔧 Utilities

  • useEventCallback — A stable callback that always calls the latest function
  • useLatest — A ref that always holds the newest value
  • usePrevious — The value from the previous render
  • useMergedRef — Combine several refs into one
  • useEventListener — Typed event subscription for window, document, elements, and media queries
  • useMount / useUnmount — Run a callback once on mount or unmount
  • useEffectOnce — Run an effect once (not StrictMode-guarded)
  • useUpdateEffect — Run an effect on updates but not on mount
  • useIsFirstRender — Whether this is the first render
  • useIsMounted — Whether the component is still mounted

🎨 Theme & Appearance

  • useDarkMode — Dark mode toggle persisted to localStorage
  • useTernaryDarkMode — Light / dark / system theme management

🔧 Next.js & SSR

  • useHydrated — Whether React has hydrated — gate client-only UI on this
  • useIsClient — Whether a DOM is available
  • useSsr — isBrowser / isServer / isHydrated together
  • useIsomorphicLayoutEffect — useLayoutEffect in the browser, useEffect on the server

💡 Usage Examples

State Management

import { useBoolean, useCounter, useArray } from "@gmana/react-hooks"

function StateExample() {
  // Boolean state with helpers
  const { value: isOpen, setTrue: open, setFalse: close, toggle } = useBoolean(false)

  // Counter with operations
  const { count, increment, decrement, reset } = useCounter(0)

  // Array management
  const { array, push, remove, filter, clear } = useArray([1, 2, 3])

  return (
    <div>
      <button onClick={toggle}>{isOpen ? "Close" : "Open"}</button>
      <button onClick={increment}>Count: {count}</button>
      <div>Array: {array.join(", ")}</div>
      <button onClick={() => push(Math.random())}>Add Random</button>
    </div>
  )
}

UI Interactions

import { useHover, useOnClickOutside, useElementSize } from "@gmana/react-hooks"

function UIExample() {
  const [setRef, { width, height }] = useElementSize()
  const hoverRef = useRef(null)
  const isHovered = useHover(hoverRef)

  const dropdownRef = useRef(null)
  const [isDropdownOpen, setIsDropdownOpen] = useState(false)

  useOnClickOutside(dropdownRef, () => setIsDropdownOpen(false))

  return (
    <div ref={setRef}>
      <p>
        Element size: {width}x{height}
      </p>

      <div ref={hoverRef} style={{ background: isHovered ? "blue" : "gray" }}>
        Hover me!
      </div>

      <div ref={dropdownRef}>
        <button onClick={() => setIsDropdownOpen(!isDropdownOpen)}>Toggle Dropdown</button>
        {isDropdownOpen && <div>Dropdown content</div>}
      </div>
    </div>
  )
}

Data Fetching

import { useFetch, useDebounce } from "@gmana/react-hooks"

function DataExample() {
  const [query, setQuery] = useState("")
  const debouncedQuery = useDebounce(query, 500)

  const { data, error } = useFetch(debouncedQuery ? `https://api.example.com/search?q=${debouncedQuery}` : null)

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search..." />
      {error && <p>Error: {error.message}</p>}
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  )
}

Theme Management

import { useDarkMode, useTernaryDarkMode } from "@gmana/react-hooks"

function ThemeExample() {
  // Simple dark mode
  const { isDarkMode, toggle, enable, disable } = useDarkMode()

  // Advanced theme with system preference
  const { isDarkMode: isAdvancedDark, ternaryDarkMode, setTernaryDarkMode } = useTernaryDarkMode()

  return (
    <div className={isDarkMode ? "dark" : "light"}>
      <button onClick={toggle}>{isDarkMode ? "Light" : "Dark"} Mode</button>

      <select value={ternaryDarkMode} onChange={(e) => setTernaryDarkMode(e.target.value)}>
        <option value="light">Light</option>
        <option value="dark">Dark</option>
        <option value="system">System</option>
      </select>
    </div>
  )
}

Next.js Integration

import { useHydrated, useMediaQuery } from "@gmana/react-hooks"

function NextJSExample() {
  const isHydrated = useHydrated()
  const isMobile = useMediaQuery("(max-width: 768px)")

  // Prevent hydration mismatches
  if (!isHydrated) {
    return <div>Loading...</div>
  }

  return (
    <div>
      <h1>My Next.js App</h1>
      {isMobile ? <MobileLayout /> : <DesktopLayout />}
    </div>
  )
}

⚠️ Migrating

Most of this release is additive — new hooks, new options, and bug fixes that need no code changes. The following are the deliberate breaking changes.

useEstimateStorage returns an object

It used to return the remaining megabytes as a bare number, which discarded the usage and quota figures the browser already provided.

// Before
const remainingMB = useEstimateStorage()

// After
const { estimate, refresh } = useEstimateStorage()
const remainingMB = estimate?.remainingMB ?? 0

useScreen returns a plain object, not Screen

Screen's properties live on its prototype, so the live object never compared unequal and React skipped the re-render. The values are now copied into a plain ScreenInfo. Reading screen?.width / screen?.height is unchanged; reading exotic Screen members is not supported.

useThrottle's third argument is now options

The old deps array is no longer needed — the callback is kept in a ref — and is still accepted and ignored, so existing call sites keep working. New code should pass { leading, trailing } instead. The returned function now also carries flush and cancel.

useCopyToClipboard resolves to a boolean

copyToClipboard now returns Promise<boolean> rather than void, and the hook additionally returns copiedValue and reset. Existing calls that ignore the return value are unaffected.

useIsMobile takes an options object

useIsMobile() still works. A custom breakpoint is now useIsMobile({ breakpoint: 1024 }).

useIntersection is now useInView

useIntersection remains as a deprecated alias with the same signature.

Hydration behaviour changed (intentionally)

useLocalStorage, useReadLocalStorage, useMediaQuery, useIsMobile, useWindowSize, and useScreen now report their fallback value during the first client render and the real value immediately after hydration. This is what makes them hydration-safe. If your app is client-only and you want the old read-immediately behaviour, opt in explicitly:

const [value] = useLocalStorage('key', 'default', { initializeWithValue: true })
const isDesktop = useMediaQuery('(min-width: 768px)', { initializeWithValue: true })
const size = useWindowSize({ initializeWithValue: true })

useSsr().isHydrated now means what it says

It previously returned true on the very first client render — the exact moment the markup must still match the server's — so using it as documented caused the mismatch it claimed to prevent. It now flips to true only after hydration.

🛠️ Development

Prerequisites

  • Node.js ≥ 18
  • Bun ≥ 1.0.0 (recommended) or npm/yarn

Setup

# Clone the repository
git clone https://github.com/sun-sreng/npm-gmana-react-hooks.git
cd npm-gmana-react-hooks

# Install dependencies
bun install

# Start development mode
bun run dev

Scripts

  • bun run dev - Start development mode with watch
  • bun run build - Build the library
  • bun run test - Run tests
  • bun run test:watch - Run tests in watch mode
  • bun run lint - Lint code
  • bun run format - Format code with oxfmt
  • bun run typecheck - Type-check src/
  • bun run check - Run lint, format check, and typecheck

Testing

We use Vitest for testing with React Testing Library:

# Run all tests
bun run test

# Run tests in watch mode
bun run test:watch

# Run specific test file
bun run test src/lib/use-boolean.test.ts

Building

The library is built with tsdown (Rolldown) and compiled through the React Compiler:

bun run build

This generates:

  • dist/index.js - ES module bundle
  • dist/index.d.ts - TypeScript declarations

📋 Requirements

Peer Dependencies

  • React ≥ 18.0.0 (useSyncExternalStore)
  • React DOM ≥ 18.0.0
  • TypeScript ≥ 5.9.3 (for TypeScript projects)

Browser Support

  • Modern browsers with ES2022 support
  • React Native (most hooks)
  • Next.js 13+ with App Router and Pages Router
  • Server-Side Rendering environments

🤝 Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

Quick Contribution Guide

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/new-hook
  3. Add your hook with tests and documentation
  4. Run tests: bun run test
  5. Submit a pull request

Adding a New Hook

  1. Create your hook in src/lib/use-your-hook.ts
  2. Add comprehensive tests in src/lib/use-your-hook.test.ts
  3. Export the hook in src/index.ts
  4. Update this README with documentation

📄 License

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

🙏 Acknowledgments

  • React team for the amazing hooks API
  • All contributors who helped build this library
  • The open-source community for inspiration and feedback

📖 Documentation

📞 Support


Made with ❤️ by Sun Sreng