hookify-react
Version:
A collection of optimized and reusable React hooks for state management, dom interaction, responsive design, storage, location, asynchronous management and performance improvements.
243 lines (179 loc) ⢠8.99 kB
Markdown
š A collection of **high-performance, reusable, and production-ready React hooks** to simplify state management, DOM interaction, location, async management and browser storage.



[](https://opensource.org/licenses/MIT)

- [Features](
- [Installation](
- [Quick Start](
- [Available Hooks](
- [API Reference](
- [Contributing](
- [License](
- [Contact](
- ā
Fully typed with TypeScript
- ā
**SSR-safe** ā works with Next.js, Remix and other server-rendered frameworks
- ā
No external dependencies (only `react` as 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
---
## <a id="installation"></a> š Installation
```sh
npm install hookify-react
```
or
```sh
yarn add hookify-react
```
> **Peer dependencies:** `react >= 18` and `react-dom >= 18`.
```tsx
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>;
}
```
| 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` |
```ts
// 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;
}
```
```ts
// 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): void
```
```ts
// 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
}
```
```ts
useSize<T extends HTMLElement>(): {
ref: Ref<T>;
size: { width; height; top; left; bottom; right } | null;
}
useWindowSize(): { width: number; height: number }
```
```ts
// `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]
```
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.
```ts
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>]
```
```ts
// 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 }
```
- **`useCopyToClipboard`** ā `error` is now an `Error | null` (previously a `string`). Render `error.message`. `copy()` now resolves to a `boolean`.
- **`useOnlineStatus`** ā now also exposes `isOnline`. The existing `onlineStatus` field is unchanged.
- **`useStorage`** ā the third argument is now `"local" | "session"` instead of a `Storage` object. `useLocalStorage`/`useSessionStorage` are unchanged.
We welcome contributions! If you have suggestions for improvements or new hooks, please open an issue or submit a pull request.
```sh
npm install
npm run test
npm run lint
npm run build
```
1. Fork the repository.
2. Create your feature branch (`git checkout -b feature/AmazingFeature`).
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`).
4. Push to the branch (`git push origin feature/AmazingFeature`).
5. Open a pull request.
## <a id="license"></a> š License
This project is licensed under the MIT License ā see the [LICENSE](LICENSE) file for details.
For any inquiries or support, please reach out to uttamakwana4503@gmail.com.
Visit [hookify-react](https://hookify-react.netlify.app) for full documentation.