Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<img src="https://img.shields.io/badge/SSR--safe-61DAFB?style=for-the-badge" alt="SSR-safe" />
<img src="https://img.shields.io/badge/tree--shakable-0A0D12?style=for-the-badge" alt="tree-shakable" />
<img src="https://img.shields.io/badge/ESM_%2B_CJS-61DAFB?style=for-the-badge" alt="ESM + CJS" />
<img src="https://img.shields.io/badge/41_hooks-0A0D12?style=for-the-badge" alt="41 hooks" />
<img src="https://img.shields.io/badge/65_hooks-0A0D12?style=for-the-badge" alt="65 hooks" />
</p>

<p align="center">
Expand Down Expand Up @@ -119,6 +119,30 @@ function Component() {
- **[`useScript`](https://hookli.vercel.app/docs/use-script)** — Load an external script and report its load status.
- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** — Declarative fetch with loading and error status.
- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** — Browser geolocation state.
- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** — Track a value from the previous render.
- **[`useList`](https://hookli.vercel.app/docs/use-list)** — Array state with push, insert, update, remove, and clear helpers.
- **[`useSet`](https://hookli.vercel.app/docs/use-set)** — Set state with add, remove, toggle, has, and clear helpers.
- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** — Throttle a fast-changing value to at most one update per interval.
- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** — A useEffect that skips the initial mount and runs only on updates.
- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** — Run an effect exactly once, on mount.
- **[`useKeyPress`](https://hookli.vercel.app/docs/use-key-press)** — Track whether a specific key is currently held down.
- **[`useWindowScroll`](https://hookli.vercel.app/docs/use-window-scroll)** — Track the window scroll position reactively.
- **[`useAsync`](https://hookli.vercel.app/docs/use-async)** — Run an async function and track its loading, error, and value state.
- **[`useMutation`](https://hookli.vercel.app/docs/use-mutation)** — Run an async write action on demand and track status, data, and error.
- **[`usePagination`](https://hookli.vercel.app/docs/use-pagination)** — Page, page size, total pages, navigation helpers, and the current item range.
- **[`useNetworkState`](https://hookli.vercel.app/docs/use-network-state)** — Track online/offline status and connection details.
- **[`usePageVisibility`](https://hookli.vercel.app/docs/use-page-visibility)** — Track whether the page/tab is currently visible.
- **[`useIdle`](https://hookli.vercel.app/docs/use-idle)** — Detect user inactivity after a configurable threshold.
- **[`useQueue`](https://hookli.vercel.app/docs/use-queue)** — FIFO queue state with add, remove, clear, and first/last/size.
- **[`useDefault`](https://hookli.vercel.app/docs/use-default)** — useState that falls back to a default when the value is nullish.
- **[`useRafState`](https://hookli.vercel.app/docs/use-raf-state)** — useState whose updates are batched to the next animation frame.
- **[`useDeepCompareEffect`](https://hookli.vercel.app/docs/use-deep-compare-effect)** — useEffect that compares dependencies by deep structural equality.
- **[`useTextSelection`](https://hookli.vercel.app/docs/use-text-selection)** — Track the text the user has currently selected on the page.
- **[`useLongPress`](https://hookli.vercel.app/docs/use-long-press)** — Detect a long press (mouse or touch) via spreadable handlers.
- **[`useHotkeys`](https://hookli.vercel.app/docs/use-hotkeys)** — Bind a keyboard shortcut combo (e.g. ctrl+k) to a callback.
- **[`useFullscreen`](https://hookli.vercel.app/docs/use-fullscreen)** — Control the Fullscreen API for an element and track its state.
- **[`useBattery`](https://hookli.vercel.app/docs/use-battery)** — Read device battery level and charging state (where supported).
- **[`usePermission`](https://hookli.vercel.app/docs/use-permission)** — Query a Permissions API permission and track its state.

## 🧪 TypeScript

Expand Down
Binary file modified apps/docs/public/hookli-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 54 additions & 43 deletions apps/docs/public/hookli-banner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/hookli-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 54 additions & 43 deletions assets/hookli-banner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 25 additions & 1 deletion packages/hookli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<img src="https://img.shields.io/badge/SSR--safe-61DAFB?style=for-the-badge" alt="SSR-safe" />
<img src="https://img.shields.io/badge/tree--shakable-0A0D12?style=for-the-badge" alt="tree-shakable" />
<img src="https://img.shields.io/badge/ESM_%2B_CJS-61DAFB?style=for-the-badge" alt="ESM + CJS" />
<img src="https://img.shields.io/badge/41_hooks-0A0D12?style=for-the-badge" alt="41 hooks" />
<img src="https://img.shields.io/badge/65_hooks-0A0D12?style=for-the-badge" alt="65 hooks" />
</p>

<p align="center">
Expand Down Expand Up @@ -117,6 +117,30 @@ function Component() {
- **[`useScript`](https://hookli.vercel.app/docs/use-script)** — Load an external script and report its load status.
- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** — Declarative fetch with loading and error status.
- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** — Browser geolocation state.
- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** — Track a value from the previous render.
- **[`useList`](https://hookli.vercel.app/docs/use-list)** — Array state with push, insert, update, remove, and clear helpers.
- **[`useSet`](https://hookli.vercel.app/docs/use-set)** — Set state with add, remove, toggle, has, and clear helpers.
- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** — Throttle a fast-changing value to at most one update per interval.
- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** — A useEffect that skips the initial mount and runs only on updates.
- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** — Run an effect exactly once, on mount.
- **[`useKeyPress`](https://hookli.vercel.app/docs/use-key-press)** — Track whether a specific key is currently held down.
- **[`useWindowScroll`](https://hookli.vercel.app/docs/use-window-scroll)** — Track the window scroll position reactively.
- **[`useAsync`](https://hookli.vercel.app/docs/use-async)** — Run an async function and track its loading, error, and value state.
- **[`useMutation`](https://hookli.vercel.app/docs/use-mutation)** — Run an async write action on demand and track status, data, and error.
- **[`usePagination`](https://hookli.vercel.app/docs/use-pagination)** — Page, page size, total pages, navigation helpers, and the current item range.
- **[`useNetworkState`](https://hookli.vercel.app/docs/use-network-state)** — Track online/offline status and connection details.
- **[`usePageVisibility`](https://hookli.vercel.app/docs/use-page-visibility)** — Track whether the page/tab is currently visible.
- **[`useIdle`](https://hookli.vercel.app/docs/use-idle)** — Detect user inactivity after a configurable threshold.
- **[`useQueue`](https://hookli.vercel.app/docs/use-queue)** — FIFO queue state with add, remove, clear, and first/last/size.
- **[`useDefault`](https://hookli.vercel.app/docs/use-default)** — useState that falls back to a default when the value is nullish.
- **[`useRafState`](https://hookli.vercel.app/docs/use-raf-state)** — useState whose updates are batched to the next animation frame.
- **[`useDeepCompareEffect`](https://hookli.vercel.app/docs/use-deep-compare-effect)** — useEffect that compares dependencies by deep structural equality.
- **[`useTextSelection`](https://hookli.vercel.app/docs/use-text-selection)** — Track the text the user has currently selected on the page.
- **[`useLongPress`](https://hookli.vercel.app/docs/use-long-press)** — Detect a long press (mouse or touch) via spreadable handlers.
- **[`useHotkeys`](https://hookli.vercel.app/docs/use-hotkeys)** — Bind a keyboard shortcut combo (e.g. ctrl+k) to a callback.
- **[`useFullscreen`](https://hookli.vercel.app/docs/use-fullscreen)** — Control the Fullscreen API for an element and track its state.
- **[`useBattery`](https://hookli.vercel.app/docs/use-battery)** — Read device battery level and charging state (where supported).
- **[`usePermission`](https://hookli.vercel.app/docs/use-permission)** — Query a Permissions API permission and track its state.

## 🧪 TypeScript

Expand Down
69 changes: 69 additions & 0 deletions packages/hookli/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# hookli — Roadmap

Tracking doc for growing the hook catalog and the package DX. Work proceeds in **tiers**;
check items off as they land. Each hook has three tracks:

- **lib** — hook + colocated vitest test + barrel + manifest entry (ships to npm)
- **docs** — rich doc-page entry in `apps/docs/lib/hook-docs.ts` (usage, API tables, live demo)
- *(the docs sidebar/index/count auto-derive from the manifest — a hook appears the moment `lib` lands; `docs` just adds the demo + usage)*

> Count: **41 → 65** (Tiers 1–3 lib all shipped ✅). Next open track: per-hook **docs demos**.

---

## Tier 1 — expected-pair gaps (the "why isn't this here?" hooks)

| hook | category | lib | docs | notes |
|---|---|:--:|:--:|---|
| `useThrottle` | effects | ✅ | ⬜ | the twin of `useDebounce` |
| `usePrevious` | state | ✅ | ⬜ | previous render's value |
| `useUpdateEffect` | effects | ✅ | ⬜ | effect that skips first render |
| `useEffectOnce` | effects | ✅ | ⬜ | run-once effect |
| `useList` | state | ✅ | ⬜ | array state (push/remove/clear) — sibling of `useMap` |
| `useSet` | state | ✅ | ⬜ | Set state — completes map/set/list trio |
| `useKeyPress` | dom | ✅ | ⬜ | true while a key is held |
| `useWindowScroll` | dom | ✅ | ⬜ | reactive `{ x, y }` scroll position |

> **Tier 1 lib: ✅ shipped** (41 → 49 hooks, +20 tests, gate green). Docs demos pending.

## Tier 2 — fill the thin `data` category (2 → 8)

| hook | category | lib | docs | notes |
|---|---|:--:|:--:|---|
| `useAsync` | data | ✅ | ⬜ | run an async fn → `{ loading, error, value }` |
| `useMutation` | data | ✅ | ⬜ | async write action with status |
| `usePagination` | data | ✅ | ⬜ | page/limit/offset state helper |
| `useNetworkState` | data | ✅ | ⬜ | online/offline + connection info |
| `usePageVisibility` | effects | ✅ | ⬜ | tab focus/blur via `visibilitychange` |
| `useIdle` | dom | ✅ | ⬜ | user-inactivity detection |

> **Tier 2 lib: ✅ shipped** (49 → 55; `data` category 2 → 8).

## Tier 3 — rounding-out

| hook | category | lib | docs | notes |
|---|---|:--:|:--:|---|
| `useQueue` | state | ✅ | ⬜ | FIFO queue |
| `useDefault` | state | ✅ | ⬜ | state with nullish fallback |
| `useRafState` | state | ✅ | ⬜ | state batched to rAF |
| `useDeepCompareEffect` | effects | ✅ | ⬜ | effect with deep-equal deps |
| `useTextSelection` | dom | ✅ | ⬜ | current selected text |
| `useLongPress` | dom | ✅ | ⬜ | long-press handlers |
| `useHotkeys` | dom | ✅ | ⬜ | keyboard combos (`ctrl+k`) |
| `useFullscreen` | dom | ✅ | ⬜ | Fullscreen API control |
| `useBattery` | data | ✅ | ⬜ | battery level/charging |
| `usePermission` | data | ✅ | ⬜ | Permissions API state |

> **Tier 3 lib: ✅ shipped** (55 → 65). All three tiers' hooks are on npm-ready `main`
> once merged; per-hook **docs demos** remain the open `docs` track across all tiers.

---

## Package & DX enhancements (not hooks)

- ⬜ `size-limit` in CI + a per-hook gzipped-size badge ("every hook < 0.5 kB")
- ⬜ Hook generator script (`pnpm new:hook use-foo`) — scaffolds folder + test + manifest entry
- ⬜ Per-hook subpath exports (`import { useToggle } from "hookli/use-toggle"`)
- ⬜ Changesets — automated versioning + changelog (also fixes git-tag ↔ npm drift)
- ⬜ "Compare vs react-use / usehooks-ts" table in the docs
- ⬜ StackBlitz "open in playground" per hook page
168 changes: 168 additions & 0 deletions packages/hookli/hooks.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,174 @@
"description": "Browser geolocation state.",
"category": "data",
"signature": "useGeoLocation(): { location: GeolocationPosition | null; error: GeolocationError | Error | null }"
},
{
"slug": "use-previous",
"name": "usePrevious",
"description": "Track a value from the previous render.",
"category": "state",
"signature": "usePrevious<T>(value: T): T | undefined"
},
{
"slug": "use-list",
"name": "useList",
"description": "Array state with push, insert, update, remove, and clear helpers.",
"category": "state",
"signature": "useList<T>(initial?: T[]): [T[], UseListActions<T>]"
},
{
"slug": "use-set",
"name": "useSet",
"description": "Set state with add, remove, toggle, has, and clear helpers.",
"category": "state",
"signature": "useSet<T>(initial?: Iterable<T>): [Set<T>, UseSetActions<T>]"
},
{
"slug": "use-throttle",
"name": "useThrottle",
"description": "Throttle a fast-changing value to at most one update per interval.",
"category": "effects",
"signature": "useThrottle<T>(value: T, interval?: number): T"
},
{
"slug": "use-update-effect",
"name": "useUpdateEffect",
"description": "A useEffect that skips the initial mount and runs only on updates.",
"category": "effects",
"signature": "useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void"
},
{
"slug": "use-effect-once",
"name": "useEffectOnce",
"description": "Run an effect exactly once, on mount.",
"category": "effects",
"signature": "useEffectOnce(effect: EffectCallback): void"
},
{
"slug": "use-key-press",
"name": "useKeyPress",
"description": "Track whether a specific key is currently held down.",
"category": "dom",
"signature": "useKeyPress(targetKey: string): boolean"
},
{
"slug": "use-window-scroll",
"name": "useWindowScroll",
"description": "Track the window scroll position reactively.",
"category": "dom",
"signature": "useWindowScroll(): { x: number; y: number }"
},
{
"slug": "use-async",
"name": "useAsync",
"description": "Run an async function and track its loading, error, and value state.",
"category": "data",
"signature": "useAsync<T>(asyncFunction: () => Promise<T>, immediate?: boolean): UseAsyncReturn<T>"
},
{
"slug": "use-mutation",
"name": "useMutation",
"description": "Run an async write action on demand and track status, data, and error.",
"category": "data",
"signature": "useMutation<TArgs, TData>(mutationFn: (...args: TArgs) => Promise<TData>): UseMutationReturn<TArgs, TData>"
},
{
"slug": "use-pagination",
"name": "usePagination",
"description": "Page, page size, total pages, navigation helpers, and the current item range.",
"category": "data",
"signature": "usePagination(options?: UsePaginationOptions): UsePaginationReturn"
},
{
"slug": "use-network-state",
"name": "useNetworkState",
"description": "Track online/offline status and connection details.",
"category": "data",
"signature": "useNetworkState(): NetworkState"
},
{
"slug": "use-page-visibility",
"name": "usePageVisibility",
"description": "Track whether the page/tab is currently visible.",
"category": "effects",
"signature": "usePageVisibility(): boolean"
},
{
"slug": "use-idle",
"name": "useIdle",
"description": "Detect user inactivity after a configurable threshold.",
"category": "dom",
"signature": "useIdle(ms?: number): boolean"
},
{
"slug": "use-queue",
"name": "useQueue",
"description": "FIFO queue state with add, remove, clear, and first/last/size.",
"category": "state",
"signature": "useQueue<T>(initial?: T[]): UseQueueReturn<T>"
},
{
"slug": "use-default",
"name": "useDefault",
"description": "useState that falls back to a default when the value is nullish.",
"category": "state",
"signature": "useDefault<T>(initialValue: T | null | undefined, defaultValue: T): [T, (value: T | null | undefined) => void]"
},
{
"slug": "use-raf-state",
"name": "useRafState",
"description": "useState whose updates are batched to the next animation frame.",
"category": "state",
"signature": "useRafState<T>(initialState: T | (() => T)): [T, Dispatch<SetStateAction<T>>]"
},
{
"slug": "use-deep-compare-effect",
"name": "useDeepCompareEffect",
"description": "useEffect that compares dependencies by deep structural equality.",
"category": "effects",
"signature": "useDeepCompareEffect(effect: EffectCallback, deps: DependencyList): void"
},
{
"slug": "use-text-selection",
"name": "useTextSelection",
"description": "Track the text the user has currently selected on the page.",
"category": "dom",
"signature": "useTextSelection(): string"
},
{
"slug": "use-long-press",
"name": "useLongPress",
"description": "Detect a long press (mouse or touch) via spreadable handlers.",
"category": "dom",
"signature": "useLongPress(callback: (event: MouseEvent | TouchEvent) => void, options?: UseLongPressOptions): UseLongPressHandlers"
},
{
"slug": "use-hotkeys",
"name": "useHotkeys",
"description": "Bind a keyboard shortcut combo (e.g. ctrl+k) to a callback.",
"category": "dom",
"signature": "useHotkeys(keys: string, callback: (event: KeyboardEvent) => void): void"
},
{
"slug": "use-fullscreen",
"name": "useFullscreen",
"description": "Control the Fullscreen API for an element and track its state.",
"category": "dom",
"signature": "useFullscreen<T extends HTMLElement>(ref: RefObject<T | null>): UseFullscreenReturn"
},
{
"slug": "use-battery",
"name": "useBattery",
"description": "Read device battery level and charging state (where supported).",
"category": "data",
"signature": "useBattery(): BatteryState"
},
{
"slug": "use-permission",
"name": "usePermission",
"description": "Query a Permissions API permission and track its state.",
"category": "data",
"signature": "usePermission(name: PermissionName): UsePermissionResult"
}
]
}
27 changes: 19 additions & 8 deletions packages/hookli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hookli",
"version": "1.5.0",
"description": "hookli — all the React.js custom hooks you frequently use.",
"version": "1.6.0",
"description": "Every React hook you reach for, in one typed, zero-dependency, tree-shakable package — state, effects, DOM & data. SSR-safe, ESM + CJS.",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
Expand All @@ -19,15 +19,26 @@
},
"keywords": [
"hookli",
"react",
"react-hooks",
"hooks",
"react.js",
"custom-hooks",
"Custom Hooks",
"node.js",
"Javascript",
"Frontend Development",
"Productivity"
"usehooks",
"typescript",
"typed",
"ssr",
"nextjs",
"tree-shakable",
"esm",
"zero-dependency",
"useDebounce",
"useThrottle",
"useLocalStorage",
"useAsync",
"useHotkeys",
"usePrevious",
"useMediaQuery",
"frontend"
],
"author": "Saif Mohamed <saifmohamed.dev@gmail.com> (https://linkedin.com/in/saifmohamedsv/)",
"license": "ISC",
Expand Down
Loading
Loading