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

@ox0/hooks

v0.2.1

Published

Shared React hooks for ox0 applications. Every hook is SSR-safe unless noted.

Downloads

332

Readme

@ox0/hooks

Shared React hooks for ox0 applications. Every hook is SSR-safe unless noted.

Installation

pnpm add @ox0/hooks

Hooks

State and control

useControllableState<T>(options)

Controlled/uncontrolled state bridge. Pass value + onChange for controlled mode; omit value for uncontrolled. When uncontrolled, internal state resets to defaultValue whenever defaultValue changes — pass stable primitives to avoid surprise resets.

const [val, setVal] = useControllableState({ defaultValue: "initial", value, onChange })

useDisclosure(initialOpen?)

Boolean open/close state. Returns { isOpen, onOpen, onClose, onToggle } with stable callbacks.

const { isOpen, onOpen, onClose } = useDisclosure()

Persistence

All storage hooks require a <StorageProvider scope={userId ?? null}> ancestor. The scope string prefixes every key so different users sharing a browser do not mix data. Pass scope={null} to opt out of scoping.

import { StorageProvider } from "@ox0/hooks"

function App() {
  return <StorageProvider scope={currentUserId}>{children}</StorageProvider>
}

usePersistentState(key, parser, options)

localStorage, tab-private. Changes are not visible in other open tabs.

usePersistentSharedState(key, parser, options)

localStorage, shared across tabs. Changes broadcast via BroadcastChannel and the storage event. All tabs with the same key stay in sync.

useSessionState(key, parser, options)

sessionStorage, tab-private.

useSessionSharedState(key, parser, options)

sessionStorage, shared within the tab (multiple hook instances on the same key sync via in-tab pub/sub).

Shared options:

type StoredStateOptions<T> = {
  defaultValue: T
  version?: number // @default 1
  migrate?: (value: unknown, fromVersion: number) => T
}

Pass any object with a .parse(unknown): T method as the parser (Zod schemas work directly). Use version + migrate to transform stored data when the shape changes:

const [theme, setTheme] = usePersistentSharedState("ui:theme", ThemeSchema, {
  defaultValue: "system",
  version: 2,
  migrate: (old, from) => (from === 1 ? "system" : "system"),
})

Interaction

usePress(props) — re-export from React Aria

Cross-device press normalization for mouse, touch, keyboard (Space/Enter), and virtual pointer (screen reader). Handles iOS text-selection conflicts and scroll-gesture cancellation. Prefer this over raw onClick for custom interactive elements.

useLongPress(options)

Returns pointer event handlers that fire onLongPress after the pointer has been held for delayMs (default 500ms) without moving beyond moveThresholdPx (default 8px). Cancels on pointer up or excessive movement; suppresses the subsequent click.

useKeyDown(key, callback, options?)

Attaches a keydown listener to window. Modifier options: ctrl, meta, shift, alt. Pass enabled: false to disable without removing the hook call.


DOM and browser

useContainerSize<T extends HTMLElement>()

Measures element content size via ResizeObserver. Returns [ref, { width, height }]. Attach the ref to the element to observe.

const [ref, { width }] = useContainerSize<HTMLDivElement>()

useEventListener(type, listener, target?)

Attaches a typed event listener to window (default), a Document, HTMLElement, or a ref. The listener ref is always up to date — no stale-closure issues.

useHover<T extends HTMLElement>()

Returns [ref, isHovered]. Uses pointerenter/pointerleave events.


Viewport and layout

useBreakpoint()

Tracks the current responsive breakpoint. Returns { breakpoint, isMobile, isTablet, isDesktop, isMobileOrTablet, isTabletOrDesktop }.

Breakpoints: mobile ≤ 767px, tablet ≤ 1023px, desktop > 1023px.

useWindowSize()

Tracks { width, height } from window.innerWidth/innerHeight.

useViewport()

Tracks { visualViewportHeight, keyboardOpen }. keyboardOpen is true when the on-screen keyboard reduces the visual viewport by more than 150px — useful for mobile layouts.


Media queries and preferences

useMediaQuery(query)

Returns true when the CSS media query matches. Updates on change.

usePointerCoarse()

Returns true when the primary pointer is coarse (touch screen). Shorthand for useMediaQuery("(pointer: coarse)").

usePrefersReducedMotion()

Returns true when the user has requested reduced motion.

useNavigatorOnline()

Returns true when the browser reports a network connection. Updates on online/offline events.


OS and environment

useOS(override?)

Detects the runtime OS: "mac" | "windows" | "linux" | "mobile". Useful for keyboard shortcut labels. Pass override to force a value in tests or demos.


Timing

useDebouncedValue<T>(value, delay?)

Returns a debounced copy of value that only updates after delay ms (default 200ms) of no changes.

useInterval(callback, delay)

Calls callback every delay ms. Pass delay = null to pause. Callback ref is always current.

useTimeout(callback, delay)

Calls callback once after delay ms. Pass delay = null to disable. Callback ref is always current.

useNow(options?)

Returns the current Date.now() value, updating every update ms (default 1000ms). Pass update: false to get a stable snapshot.


Utilities

usePrevious<T>(value)

Returns the value from the previous render. Returns undefined on the first render.

useCopyToClipboard(resetMs?)

Returns { copy: (text: string) => Promise<void>, copied: boolean }. copied resets to false after resetMs ms (default 2000ms).

useBeforeUnloadWarning(enabled)

Shows the browser's native "leave page?" dialog when enabled is true. Use for unsaved-changes guards.

useDeferredPresence(options)

Manages show/hide lifecycle with entry delay, minimum visible time, and exit duration. Returns "hidden" | "visible" | "exiting". Useful for tooltips, toasts, and animated presence.