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.
Maintainers
Keywords
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.
🌟 Table of Contents
🚀 Features
- ✅ Fully typed with TypeScript
- ✅ SSR-safe — works with Next.js, Remix and other server-rendered frameworks
- ✅ No external dependencies (only
reactas 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-reactor
yarn add hookify-reactPeer dependencies:
react >= 18andreact-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): voidDOM 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)
useCopyToClipboard—erroris now anError | null(previously astring). Rendererror.message.copy()now resolves to aboolean.useOnlineStatus— now also exposesisOnline. The existingonlineStatusfield is unchanged.useStorage— the third argument is now"local" | "session"instead of aStorageobject.useLocalStorage/useSessionStorageare 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- Fork the repository.
- Create your feature branch (
git checkout -b feature/AmazingFeature). - Commit your changes (
git commit -m 'Add some AmazingFeature'). - Push to the branch (
git push origin feature/AmazingFeature). - 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.
