useIdle
zyeon/use-idleFreeHook
Flips a boolean idle state after a period with no pointer, keyboard, wheel, or touch activity, with a throttled reset and SSR-safe cleanup.
Install it
npx shadcn@latest add https://ui.zyeon.ai/r/use-idle.jsonSource
1"use client"23import * as React from "react"45export interface UseIdleOptions {6 /** Activity events to listen for, attached to `window` as passive. Passing this replaces the default list wholesale. */7 events?: string[]8 /** Initial idle value at mount. Default `false` — treated as "activity just happened". */9 initialState?: boolean10}1112const DEFAULT_EVENTS = ["pointermove", "pointerdown", "keydown", "wheel", "touchstart"]1314// high-frequency events (pointermove/wheel) reach handleActivity on every tick; a15// clearTimeout + setTimeout per tick costs real time with many listeners or on a weak16// device. Throttle by timestamp comparison instead: activity inside the window is17// dropped and only the first activity past it resets the timer. The cost is that idle18// can fire up to one window early, which is fine for idle detection.19// the window has to narrow with timeout: a fixed 1s would call continuous activity idle20// whenever timeout < 1s (the 500ms timer expires first, while the next activity let21// through has to wait for 1s).22const throttleFor = (timeout: number) => Math.min(1000, Math.max(100, Math.floor(timeout / 3)))2324/**25 * User idle detection. Mounting starts a `timeout` ms timer; any of `events` (pointer26 * move/down, keyboard, wheel, touch by default) counts as activity — it sets `idle`27 * to `false` and restarts the timer, and the timer expiring sets it to `true`.28 *29 * `visibilitychange` is handled separately and cannot be switched off through30 * `events`: coming back to the tab (`document.visibilityState === "visible"`) counts31 * as activity and resets the timer, but leaving it (hidden) does **not** force idle —32 * the timer keeps counting real elapsed time and flips on its own, with no "away means33 * idle" special case (a background tab's timer may be throttled and fire late; that34 * drift is accepted rather than chased second by second).35 *36 * Every `setState` happens in an event or timer callback, never during render or37 * synchronously in an effect body, so it is SSR-safe; unmount clears the timer and38 * every listener.39 */40export function useIdle(timeout = 60000, options: UseIdleOptions = {}): boolean {41 const { events = DEFAULT_EVENTS, initialState = false } = options42 const [idle, setIdle] = React.useState(initialState)4344 // latest events in a ref; the effect depends on eventsKey below (contents, not the45 // array identity), so an inline array literal from a consumer does not tear the46// … truncated
Files it writes
More from zyeon
All 893 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useAsyncuse-async | zyeon | Hooks | Free | no deps | |
| useBatteryuse-battery | zyeon | Hooks | Free | no deps | |
| useBroadcastChanneluse-broadcast-channel | zyeon | Hooks | Free | no deps | |
| useClickOutsideuse-click-outside | zyeon | Hooks | Free | no deps | |
| useClipboardPasteuse-clipboard-paste | zyeon | Hooks | Free | no deps | |
| useControllableStateuse-controllable-state | zyeon | Hooks | Free | no deps | |
| useCookieuse-cookie | zyeon | Hooks | Free | no deps | |
| useCopyToClipboarduse-copy-to-clipboard | zyeon | Hooks | Free | no deps |
