hookstorm
v1.1.0
Published
A collection of production-ready, zero-dependency React hooks for state, DOM, storage, network, and utilities.
Maintainers
Readme
Installation
npm install hookstorm
# or
yarn add hookstorm
# or
pnpm add hookstormRequires React 18 or later as a peer dependency.
Quick Start
import { useToggle, useDebounce, useLocalStorage, useWindowSize } from "hookstorm";
function App() {
const { value: isOpen, toggle } = useToggle();
const { value: theme, setValue: setTheme } = useLocalStorage("theme", "light");
const { width } = useWindowSize();
const debouncedWidth = useDebounce(width, 300);
return (
<div>
<button onClick={toggle}>{isOpen ? "Close" : "Open"}</button>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle theme
</button>
<p>Window: {debouncedWidth}px</p>
</div>
);
}Hooks
State & UI
| Hook | Description |
| --- | --- |
| useToggle | Toggle a boolean with an optional forced value |
| useCounter | Increment, decrement, and reset a counter |
| usePrevious | Access the previous value of any state or prop |
| useDebounce | Delay a value update until input settles |
DOM & Browser
| Hook | Description |
| --- | --- |
| useWindowSize | Track current window width and height |
| useScrollPosition | Read the current scroll position (RAF-throttled) |
| useMousePosition | Track cursor position in real time (RAF-throttled) |
| useHover | Detect hover state on any element |
| useClickOutside | Detect clicks (and focus) outside a referenced element |
| useDocumentTitle | Dynamically update the browser tab title |
| useLockScroll | Prevent or restore page scroll — safe for multiple instances |
| useKeyPress | Detect when a specific key is held down |
| useEventListener | Attach an event listener to window or any element |
| useMediaQuery | Track whether a CSS media query matches |
| useIntersectionObserver | Detect when an element enters or leaves the viewport |
| useResizeObserver | Track the dimensions of a DOM element |
Storage
| Hook | Description |
| --- | --- |
| useLocalStorage | localStorage with a React state interface |
| useSessionStorage | sessionStorage with a React state interface |
| useCookie | Read, write, and remove cookies (RFC 6265 compliant) |
Network & Device
| Hook | Description |
| --- | --- |
| useOnlineStatus | Detect online / offline status |
| useGeoLocation | Request and track user geolocation with loading state |
| usePageVisibility | Know when the user switches away from the tab |
| usePrefersTheme | Read the OS prefers-color-scheme setting |
Timers & Async
| Hook | Description |
| --- | --- |
| useAsync | Manage async function lifecycle — data, loading, error |
| useInterval | Declarative setInterval — pause with null |
| useTimeout | Declarative setTimeout with reset and clear |
| useCountdown | Countdown timer with start, stop, and reset |
| useIdle | Detect inactivity after a configurable timeout |
| useCopyToClipboard | Copy text and track clipboard state |
| useMounted | Safe guard against post-unmount state updates |
Examples
import { useAsync } from "hookstorm";
function UserProfile({ id }: { id: string }) {
const { data, loading, error, execute } = useAsync(
() => fetch(`/api/users/${id}`).then((r) => r.json()),
);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message} <button onClick={execute}>Retry</button></p>;
return <p>Hello, {data.name}</p>;
}import { useClickOutside } from "hookstorm";
function Dropdown({ onClose }: { onClose: () => void }) {
const { ref } = useClickOutside(onClose);
return <div ref={ref}>Dropdown content</div>;
}import { useState, useEffect } from "react";
import { useDebounce } from "hookstorm";
function Search() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 400);
useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery);
}, [debouncedQuery]);
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
);
}import { useState } from "react";
import { useInterval } from "hookstorm";
function Clock() {
const [time, setTime] = useState(new Date());
useInterval(() => setTime(new Date()), 1000);
return <p>{time.toLocaleTimeString()}</p>;
}import { useLocalStorage } from "hookstorm";
function Settings() {
const { value: lang, setValue: setLang } = useLocalStorage("lang", "en");
return (
<select value={lang ?? "en"} onChange={(e) => setLang(e.target.value)}>
<option value="en">English</option>
<option value="tr">Turkish</option>
</select>
);
}import { useIdle } from "hookstorm";
function App() {
const { isIdle } = useIdle(30_000); // 30 seconds
return <p>{isIdle ? "You've been away!" : "Welcome back."}</p>;
}Why Hookstorm?
| | |
| --- | --- |
| Zero dependencies | Only React is required — nothing else ships with your bundle |
| TypeScript first | Every hook is fully typed with exported return types |
| Tree-shakeable | "sideEffects": false — bundlers only ship what you import |
| SSR safe | All browser APIs are guarded for Next.js and Remix |
| Battle-tested | 139 tests across 30 test files, 97%+ source coverage |
| MIT licensed | Free for personal and commercial use |
TypeScript
All return types are exported and ready to use:
import type {
UseAsyncReturn,
UseToggleReturn,
UseLocalStorageReturn,
UseCountdownReturn,
} from "hookstorm";Contributing
Contributions of all kinds are welcome.
- Fork the repository
- Create a branch:
git checkout -b feat/my-hook - Make your changes and add tests
- Open a pull request
When reporting a bug, please include the hook name, a minimal reproduction, and your React + TypeScript versions.
License
MIT — see LICENSE for details.
