{"id":18486294,"url":"https://github.com/maxgfr/react-essentials-functions","last_synced_at":"2026-04-12T13:57:18.253Z","repository":{"id":64207509,"uuid":"574106450","full_name":"maxgfr/react-essentials-functions","owner":"maxgfr","description":"A collection of useful hooks and components for React","archived":false,"fork":false,"pushed_at":"2025-04-04T01:14:49.000Z","size":8257,"stargazers_count":1,"open_issues_count":14,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-04T02:23:00.961Z","etag":null,"topics":["components","hooks","react","useful-functions"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/react-essentials-functions","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxgfr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-04T12:59:33.000Z","updated_at":"2025-04-04T01:12:35.000Z","dependencies_parsed_at":"2023-10-04T21:58:45.761Z","dependency_job_id":"e87160a0-a5c4-4c0d-8283-248335d7d68b","html_url":"https://github.com/maxgfr/react-essentials-functions","commit_stats":{"total_commits":231,"total_committers":3,"mean_commits":77.0,"dds":"0.030303030303030276","last_synced_commit":"6a741408c77cf80dca2b8b5a02524dfbff4d00f0"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":"maxgfr/typescript-react-lib-swc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxgfr%2Freact-essentials-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxgfr%2Freact-essentials-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxgfr%2Freact-essentials-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxgfr%2Freact-essentials-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxgfr","download_url":"https://codeload.github.com/maxgfr/react-essentials-functions/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247912789,"owners_count":21017045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["components","hooks","react","useful-functions"],"created_at":"2024-11-06T12:48:54.885Z","updated_at":"2026-04-12T13:57:18.245Z","avatar_url":"https://github.com/maxgfr.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-essentials-functions\n\nA collection of zero-dependency useful hooks and components for React.\n\n## Installation\n\n```bash\npnpm add react-essentials-functions\n# or\nyarn add react-essentials-functions\n# or\nnpm install react-essentials-functions\n```\n\n## Table of Contents\n\n- [Hooks](#hooks)\n  - [useClickOutside](#useclickoutside)\n  - [useClipboard](#useclipboard)\n  - [useCounter](#usecounter)\n  - [useDebounce](#usedebounce)\n  - [useDimensions](#usedimensions)\n  - [useDocumentTitle](#usedocumenttitle)\n  - [useEventListener](#useeventlistener)\n  - [useHover](#usehover)\n  - [useIdle](#useidle)\n  - [useIntersectionObserver](#useintersectionobserver)\n  - [useInterval](#useinterval)\n  - [useIsFirstRender](#useisfirstrender)\n  - [useKeyPress](#usekeypress)\n  - [useLocalStorage](#uselocalstorage)\n  - [useMap](#usemap)\n  - [useMediaQuery](#usemediaquery)\n  - [useOnlineStatus](#useonlinestatus)\n  - [usePrevious](#useprevious)\n  - [useSafeFetch](#usesafefetch)\n  - [useSafeState](#usesafestate)\n  - [useScript](#usescript)\n  - [useSessionStorage](#usesessionstorage)\n  - [useSet](#useset)\n  - [useTheme](#usetheme)\n  - [useTimeout](#usetimeout)\n  - [useToggle](#usetoggle)\n  - [useWindowDimensions](#usewindowdimensions)\n- [Components](#components)\n  - [ConditionalWrapper](#conditionalwrapper)\n\n---\n\n## Hooks\n\n### useClickOutside\n\nHook that detects clicks outside of a referenced element. Useful for closing dropdowns, modals, and popovers. Listens for both `mousedown` and `touchstart` events.\n\n**Parameters:**\n- `ref: RefObject\u003cHTMLElement\u003e` - React ref to the element to monitor\n- `handler: (event: MouseEvent | TouchEvent) =\u003e void` - Callback fired when a click outside is detected\n\n**Returns:**\n- `void`\n\n**Side effects:**\n- Adds `mousedown` and `touchstart` event listeners on `document`\n- Removes listeners on unmount\n\n**Example:**\n```tsx\nimport { useClickOutside } from 'react-essentials-functions';\nimport { useRef, useState } from 'react';\n\nfunction Dropdown() {\n  const dropdownRef = useRef\u003cHTMLDivElement\u003e(null);\n  const [isOpen, setIsOpen] = useState(false);\n\n  useClickOutside(dropdownRef, () =\u003e setIsOpen(false));\n\n  return (\n    \u003cdiv ref={dropdownRef}\u003e\n      \u003cbutton onClick={() =\u003e setIsOpen(true)}\u003eOpen\u003c/button\u003e\n      {isOpen \u0026\u0026 \u003cul\u003e\u003cli\u003eOption 1\u003c/li\u003e\u003cli\u003eOption 2\u003c/li\u003e\u003c/ul\u003e}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useClipboard\n\nHook that copies text to the clipboard and tracks the copy status. Provides a temporary \"copied\" feedback state that resets after a configurable delay. Uses the modern Clipboard API.\n\n**Parameters:**\n- `options?: { resetDelay?: number }` - Optional configuration\n  - `resetDelay` - Milliseconds before `copied` resets to `false` (default: `2000`)\n\n**Returns:**\n- `UseClipboardReturn` - Object containing:\n  - `copy: (text: string) =\u003e Promise\u003cvoid\u003e` - Function to copy text\n  - `copied: boolean` - Whether the last copy was successful (resets after delay)\n  - `error: Error | null` - Error from the last copy attempt\n\n**Side effects:**\n- Writes to the system clipboard via `navigator.clipboard.writeText()`\n- Sets a timeout to reset the `copied` state\n- Cleans up timeout on unmount\n\n**Example:**\n```tsx\nimport { useClipboard } from 'react-essentials-functions';\n\nfunction ShareButton({ url }: { url: string }) {\n  const { copy, copied, error } = useClipboard({ resetDelay: 3000 });\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={() =\u003e copy(url)}\u003e\n        {copied ? 'Copied!' : 'Copy link'}\n      \u003c/button\u003e\n      {error \u0026\u0026 \u003cspan\u003eFailed to copy\u003c/span\u003e}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useCounter\n\nHook for managing a numeric counter with increment, decrement, and reset. Optionally clamps values between min and max bounds.\n\n**Parameters:**\n- `initialValue?: number` - The initial counter value (default: `0`)\n- `options?: { min?: number; max?: number }` - Optional min/max bounds\n\n**Returns:**\n- `UseCounterReturn` - Object containing:\n  - `count: number` - Current count value\n  - `increment: (amount?: number) =\u003e void` - Increment by 1 or a custom amount\n  - `decrement: (amount?: number) =\u003e void` - Decrement by 1 or a custom amount\n  - `reset: () =\u003e void` - Reset to the initial value\n  - `set: (value: number | ((prev: number) =\u003e number)) =\u003e void` - Set an arbitrary value\n\n**Example:**\n```tsx\nimport { useCounter } from 'react-essentials-functions';\n\nfunction QuantitySelector() {\n  const { count, increment, decrement, reset } = useCounter(1, { min: 0, max: 99 });\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={() =\u003e decrement()}\u003e-\u003c/button\u003e\n      \u003cspan\u003e{count}\u003c/span\u003e\n      \u003cbutton onClick={() =\u003e increment()}\u003e+\u003c/button\u003e\n      \u003cbutton onClick={reset}\u003eReset\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useDebounce\n\nHook that debounces a value by a given delay. The debounced value will only update after the specified delay has passed since the last change. Useful for search inputs, API calls, and any rapid-fire updates.\n\n**Type Parameters:**\n- `T` - The type of the value to debounce\n\n**Parameters:**\n- `value: T` - The value to debounce\n- `delay: number` - The debounce delay in milliseconds\n\n**Returns:**\n- `T` - The debounced value\n\n**Example:**\n```tsx\nimport { useDebounce } from 'react-essentials-functions';\nimport { useState, useEffect } from 'react';\n\nfunction SearchComponent() {\n  const [searchTerm, setSearchTerm] = useState('');\n  const debouncedSearch = useDebounce(searchTerm, 300);\n\n  useEffect(() =\u003e {\n    if (debouncedSearch) {\n      // This only fires 300ms after the user stops typing\n      fetchResults(debouncedSearch);\n    }\n  }, [debouncedSearch]);\n\n  return (\n    \u003cinput\n      value={searchTerm}\n      onChange={(e) =\u003e setSearchTerm(e.target.value)}\n      placeholder=\"Search...\"\n    /\u003e\n  );\n}\n```\n\n---\n\n### useDimensions\n\nHook to get the dimensions of a DOM element. Uses `ResizeObserver` for optimal performance with a fallback to window events for older browsers.\n\n**Parameters:**\n- `targetRef: RefObject\u003cHTMLElement\u003e` - React ref to the element to measure\n\n**Returns:**\n- `Dimensions` - Object containing `width` and `height` of the element\n\n**Side effects:**\n- Creates a `ResizeObserver` on the target element (or falls back to `resize`/`scroll` window listeners)\n- Cleans up on unmount\n\n**Example:**\n```tsx\nimport { useDimensions } from 'react-essentials-functions';\nimport { useRef } from 'react';\n\nfunction MyComponent() {\n  const targetRef = useRef\u003cHTMLDivElement\u003e(null);\n  const { width, height } = useDimensions(targetRef);\n\n  return (\n    \u003cdiv ref={targetRef}\u003e\n      Size: {width}px x {height}px\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useDocumentTitle\n\nHook that sets the document title and optionally restores it on unmount. Useful for updating the browser tab title based on the current page or route.\n\n**Parameters:**\n- `title: string` - The document title to set\n- `options?: { restoreOnUnmount?: boolean }` - Optional configuration\n  - `restoreOnUnmount` - Whether to restore the previous title on unmount (default: `true`)\n\n**Returns:**\n- `void`\n\n**Side effects:**\n- Sets `document.title` on mount and when the title changes\n- Restores the previous title on unmount (unless `restoreOnUnmount: false`)\n\n**Example:**\n```tsx\nimport { useDocumentTitle } from 'react-essentials-functions';\n\nfunction ProfilePage({ user }: { user: { name: string } }) {\n  useDocumentTitle(`${user.name} - Profile`);\n\n  return \u003cdiv\u003e{user.name}\u003c/div\u003e;\n}\n```\n\n---\n\n### useEventListener\n\nHook that declaratively adds an event listener to a target. Automatically handles cleanup and always uses the latest handler reference without re-subscribing.\n\n**Type Parameters:**\n- `K extends keyof WindowEventMap` - The event name type\n\n**Parameters:**\n- `eventName: K` - The event name to listen for\n- `handler: (event: WindowEventMap[K]) =\u003e void` - The event handler callback\n- `target?: EventTarget | null` - The event target (default: `window`)\n- `options?: boolean | AddEventListenerOptions` - Optional `addEventListener` options\n\n**Returns:**\n- `void`\n\n**Side effects:**\n- Adds an event listener on the target\n- Removes the listener on unmount or when dependencies change\n\n**Example:**\n```tsx\nimport { useEventListener } from 'react-essentials-functions';\n\nfunction ScrollTracker() {\n  useEventListener('scroll', (event) =\u003e {\n    console.log('Scrolled!', window.scrollY);\n  });\n\n  return \u003cdiv\u003eScroll the page\u003c/div\u003e;\n}\n```\n\n---\n\n### useHover\n\nHook that tracks whether an element is being hovered. Uses `mouseenter`/`mouseleave` events for reliable hover detection. Returns a callback ref for easy attachment.\n\n**Type Parameters:**\n- `T extends HTMLElement` - The type of the element to track (default: `HTMLElement`)\n\n**Returns:**\n- `UseHoverReturn\u003cT\u003e` - Object containing:\n  - `ref: (node: T | null) =\u003e void` - Callback ref to attach to the element\n  - `isHovered: boolean` - Whether the element is currently hovered\n\n**Side effects:**\n- Adds `mouseenter` and `mouseleave` listeners on the referenced element\n- Cleans up listeners when the ref changes or on unmount\n\n**Example:**\n```tsx\nimport { useHover } from 'react-essentials-functions';\n\nfunction HoverCard() {\n  const { ref, isHovered } = useHover\u003cHTMLDivElement\u003e();\n\n  return (\n    \u003cdiv ref={ref} style={{ background: isHovered ? '#e0e0ff' : '#fff' }}\u003e\n      {isHovered ? 'Hovered!' : 'Hover me'}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useIdle\n\nHook that detects whether the user is idle (no mouse, keyboard, or touch activity for a given duration). Useful for auto-logout, pausing expensive operations, or showing \"Are you still there?\" prompts. Internally throttles activity events to avoid excessive re-renders.\n\n**Parameters:**\n- `timeout?: number` - Idle threshold in milliseconds (default: `60000` = 1 minute)\n\n**Returns:**\n- `UseIdleReturn` - Object containing:\n  - `isIdle: boolean` - Whether the user is currently idle\n  - `lastActive: number` - Timestamp (`Date.now()`) of the last detected activity\n\n**Side effects:**\n- Adds `mousemove`, `mousedown`, `keydown`, `touchstart`, and `scroll` listeners on `document`\n- Uses a `setTimeout` to detect idle state\n- Removes all listeners and clears timeout on unmount\n\n**Example:**\n```tsx\nimport { useIdle } from 'react-essentials-functions';\n\nfunction SessionGuard() {\n  const { isIdle, lastActive } = useIdle(300000); // 5 minutes\n\n  return (\n    \u003cdiv\u003e\n      {isIdle \u0026\u0026 (\n        \u003cModal\u003e\n          \u003cp\u003eAre you still there?\u003c/p\u003e\n          \u003cp\u003eLast active: {new Date(lastActive).toLocaleTimeString()}\u003c/p\u003e\n        \u003c/Modal\u003e\n      )}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useIntersectionObserver\n\nHook that observes whether a DOM element is visible in the viewport using the IntersectionObserver API. Useful for lazy loading images, infinite scroll, scroll-triggered animations, and read tracking.\n\n**Parameters:**\n- `options?: UseIntersectionObserverOptions` - Optional configuration:\n  - `threshold?: number | number[]` - Threshold(s) at which the callback is invoked (0 to 1)\n  - `root?: Element | null` - Element used as the viewport for checking visibility\n  - `rootMargin?: string` - Margin around the root element\n  - `freezeOnceVisible?: boolean` - If `true`, stops observing once the element becomes visible\n\n**Returns:**\n- `UseIntersectionObserverReturn` - Object containing:\n  - `ref: (node: Element | null) =\u003e void` - Callback ref to attach to the element\n  - `entry: IntersectionObserverEntry | null` - The latest observer entry\n  - `isIntersecting: boolean` - Whether the element is currently intersecting\n\n**Side effects:**\n- Creates an `IntersectionObserver` when a ref is attached\n- Disconnects the observer on unmount or when `freezeOnceVisible` triggers\n\n**Example:**\n```tsx\nimport { useIntersectionObserver } from 'react-essentials-functions';\n\nfunction LazyImage({ src, alt }: { src: string; alt: string }) {\n  const { ref, isIntersecting } = useIntersectionObserver({\n    threshold: 0.1,\n    freezeOnceVisible: true,\n  });\n\n  return (\n    \u003cdiv ref={ref}\u003e\n      {isIntersecting ? (\n        \u003cimg src={src} alt={alt} /\u003e\n      ) : (\n        \u003cdiv className=\"placeholder\" /\u003e\n      )}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useInterval\n\nHook that sets up a declarative `setInterval`. The interval is automatically cleared on unmount. Pass `null` as delay to pause the interval. Always uses the latest callback without resetting the interval.\n\n**Parameters:**\n- `callback: () =\u003e void` - Function to call on each interval tick\n- `delay: number | null` - Interval delay in milliseconds, or `null` to pause\n\n**Returns:**\n- `void`\n\n**Side effects:**\n- Creates a `setInterval` timer\n- Clears the interval on unmount or when delay changes\n\n**Example:**\n```tsx\nimport { useInterval } from 'react-essentials-functions';\nimport { useState } from 'react';\n\nfunction Timer() {\n  const [count, setCount] = useState(0);\n  const [isRunning, setIsRunning] = useState(true);\n\n  useInterval(() =\u003e {\n    setCount(prev =\u003e prev + 1);\n  }, isRunning ? 1000 : null);\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eCount: {count}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setIsRunning(!isRunning)}\u003e\n        {isRunning ? 'Pause' : 'Resume'}\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useIsFirstRender\n\nHook that returns `true` only on the first render. Useful for skipping effects on mount or distinguishing initial renders from subsequent updates.\n\n**Returns:**\n- `boolean` - Whether this is the first render\n\n**Example:**\n```tsx\nimport { useIsFirstRender } from 'react-essentials-functions';\nimport { useEffect } from 'react';\n\nfunction AutoSave({ data }: { data: object }) {\n  const isFirstRender = useIsFirstRender();\n\n  useEffect(() =\u003e {\n    if (!isFirstRender) {\n      saveToServer(data);\n    }\n  }, [data]);\n\n  return \u003cdiv\u003eAuto-saving...\u003c/div\u003e;\n}\n```\n\n---\n\n### useKeyPress\n\nHook that detects when a specific keyboard key is pressed. Useful for keyboard shortcuts, accessibility, modal escape-to-close, form submit-on-enter, and similar interactions.\n\n**Parameters:**\n- `targetKey: string` - The `KeyboardEvent.key` value to match (e.g., `'Escape'`, `'Enter'`, `'a'`)\n- `handler: (event: KeyboardEvent) =\u003e void` - Callback fired when the key is pressed\n- `options?: UseKeyPressOptions` - Optional configuration:\n  - `event?: 'keydown' | 'keyup'` - Which keyboard event to listen to (default: `'keydown'`)\n  - `target?: EventTarget | null` - The event target (default: `document`)\n\n**Returns:**\n- `void`\n\n**Side effects:**\n- Adds a keyboard event listener on the target (default: `document`)\n- Removes listener on unmount or when dependencies change\n\n**Example:**\n```tsx\nimport { useKeyPress } from 'react-essentials-functions';\nimport { useState } from 'react';\n\nfunction Modal({ onClose }: { onClose: () =\u003e void }) {\n  useKeyPress('Escape', onClose);\n\n  return (\n    \u003cdiv className=\"modal\"\u003e\n      \u003cp\u003ePress Escape to close\u003c/p\u003e\n      \u003cbutton onClick={onClose}\u003eClose\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useLocalStorage\n\nHook that syncs state with `localStorage`. Handles JSON serialization/deserialization automatically. Falls back gracefully when `localStorage` is unavailable (SSR, private browsing).\n\n**Type Parameters:**\n- `T` - The type of the stored value\n\n**Parameters:**\n- `key: string` - The localStorage key\n- `initialValue: T` - The initial value if nothing is stored\n\n**Returns:**\n- `[T, (value: T | ((prev: T) =\u003e T)) =\u003e void, () =\u003e void]` - A tuple containing:\n  - The current stored value\n  - A setter function (accepts value or updater function)\n  - A remove function to clear the key from localStorage\n\n**Side effects:**\n- Reads from `localStorage` on initialization\n- Writes to `localStorage` on every value change\n- `removeValue` deletes the key from `localStorage`\n\n**Example:**\n```tsx\nimport { useLocalStorage } from 'react-essentials-functions';\n\nfunction Settings() {\n  const [name, setName, removeName] = useLocalStorage('user-name', '');\n  const [preferences, setPreferences] = useLocalStorage('prefs', {\n    notifications: true,\n    language: 'en',\n  });\n\n  return (\n    \u003cdiv\u003e\n      \u003cinput value={name} onChange={(e) =\u003e setName(e.target.value)} /\u003e\n      \u003cbutton onClick={removeName}\u003eClear name\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e setPreferences(prev =\u003e ({ ...prev, language: 'fr' }))}\u003e\n        Switch to French\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useMap\n\nHook for managing a `Map` as React state. Provides convenient methods to manipulate entries without manual spread/copy boilerplate.\n\n**Type Parameters:**\n- `K` - The key type\n- `V` - The value type\n\n**Parameters:**\n- `initialEntries?: Iterable\u003c[K, V]\u003e` - Optional initial entries for the Map\n\n**Returns:**\n- `UseMapReturn\u003cK, V\u003e` - Object containing:\n  - `map: Map\u003cK, V\u003e` - The current Map\n  - `set: (key: K, value: V) =\u003e void` - Set a key-value pair\n  - `remove: (key: K) =\u003e void` - Delete a key\n  - `has: (key: K) =\u003e boolean` - Check if a key exists\n  - `get: (key: K) =\u003e V | undefined` - Get the value for a key\n  - `clear: () =\u003e void` - Clear all entries\n  - `reset: () =\u003e void` - Reset to initial entries\n  - `size: number` - Number of entries\n\n**Example:**\n```tsx\nimport { useMap } from 'react-essentials-functions';\n\nfunction ShoppingCart() {\n  const { map, set, remove, size } = useMap\u003cstring, number\u003e([\n    ['apples', 3],\n    ['bananas', 5],\n  ]);\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003e{size} items in cart\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e set('oranges', 2)}\u003eAdd oranges\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e remove('bananas')}\u003eRemove bananas\u003c/button\u003e\n      \u003cul\u003e\n        {[...map.entries()].map(([item, qty]) =\u003e (\n          \u003cli key={item}\u003e{item}: {qty}\u003c/li\u003e\n        ))}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useMediaQuery\n\nHook that tracks whether a CSS media query matches. Listens for changes and updates automatically. Useful for responsive design, detecting dark mode preference, reduced motion, etc.\n\n**Parameters:**\n- `query: string` - The CSS media query string (e.g. `'(min-width: 768px)'`)\n\n**Returns:**\n- `boolean` - Whether the media query currently matches\n\n**Side effects:**\n- Adds a `change` listener on the `MediaQueryList` object\n- Removes listener on unmount or query change\n\n**Example:**\n```tsx\nimport { useMediaQuery } from 'react-essentials-functions';\n\nfunction ResponsiveComponent() {\n  const isMobile = useMediaQuery('(max-width: 767px)');\n  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');\n  const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');\n\n  return (\n    \u003cdiv\u003e\n      {isMobile ? \u003cMobileLayout /\u003e : \u003cDesktopLayout /\u003e}\n      {prefersDark \u0026\u0026 \u003cspan\u003eDark mode detected\u003c/span\u003e}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useOnlineStatus\n\nHook that tracks whether the browser is online or offline. Updates reactively when connectivity changes. Useful for showing connectivity warnings, disabling network-dependent UI, or queueing offline mutations.\n\n**Parameters:**\n- None\n\n**Returns:**\n- `boolean` - Whether the browser is currently online\n\n**Side effects:**\n- Adds `online` and `offline` event listeners on `window`\n- Removes listeners on unmount\n\n**Example:**\n```tsx\nimport { useOnlineStatus } from 'react-essentials-functions';\n\nfunction App() {\n  const isOnline = useOnlineStatus();\n\n  return (\n    \u003cdiv\u003e\n      {!isOnline \u0026\u0026 (\n        \u003cdiv className=\"offline-banner\"\u003e\n          You are offline. Some features may be unavailable.\n        \u003c/div\u003e\n      )}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### usePrevious\n\nHook that returns the previous value of a variable. Useful for comparing current and previous props or state values.\n\n**Type Parameters:**\n- `T` - The type of the tracked value\n\n**Parameters:**\n- `value: T` - The value to track\n\n**Returns:**\n- `T | undefined` - The value from the previous render, or `undefined` on first render\n\n**Example:**\n```tsx\nimport { usePrevious } from 'react-essentials-functions';\nimport { useState } from 'react';\n\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  const previousCount = usePrevious(count);\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eCurrent: {count}, Previous: {previousCount ?? 'N/A'}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003eIncrement\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useSafeFetch\n\nHook that provides a fetch function which automatically aborts previous requests and cleans up on unmount using `AbortController`. Prevents race conditions when multiple requests are made in sequence.\n\n**Parameters:**\n- None\n\n**Returns:**\n- `(url: string, options?: RequestInit) =\u003e Promise\u003cResponse\u003e` - Fetch function with automatic abort handling\n\n**Side effects:**\n- Aborts the previous in-flight request when a new one is made\n- Aborts any pending request on component unmount\n- User-provided `signal` in options is ignored (the hook manages its own)\n\n**Example:**\n```tsx\nimport { useSafeFetch } from 'react-essentials-functions';\nimport { useEffect } from 'react';\n\nfunction DataComponent() {\n  const safeFetch = useSafeFetch();\n\n  useEffect(() =\u003e {\n    const fetchData = async () =\u003e {\n      try {\n        const response = await safeFetch('https://api.example.com/data');\n        const data = await response.json();\n      } catch (error) {\n        if (error.name !== 'AbortError') {\n          console.error('Fetch error:', error);\n        }\n      }\n    };\n\n    fetchData();\n  }, [safeFetch]);\n\n  return \u003cdiv\u003eLoading data...\u003c/div\u003e;\n}\n```\n\n---\n\n### useSafeState\n\nA version of `useState` that prevents state updates after the component unmounts, preventing memory leaks and \"Can't perform a React state update on an unmounted component\" warnings.\n\n**Type Parameters:**\n- `T` - The type of the state value\n\n**Parameters:**\n- `initialValue: T | (() =\u003e T)` - The initial state value\n\n**Returns:**\n- `[T, (value: T | ((prevState: T) =\u003e T)) =\u003e void]` - A tuple containing the current state and a safe setState function\n\n**Example:**\n```tsx\nimport { useSafeState } from 'react-essentials-functions';\nimport { useEffect } from 'react';\n\nfunction UserProfile({ userId }) {\n  const [user, setUser] = useSafeState\u003cUser | null\u003e(null);\n\n  useEffect(() =\u003e {\n    fetchUser(userId).then((data) =\u003e {\n      // Safe even if component unmounted during fetch\n      setUser(data);\n    });\n  }, [userId]);\n\n  return user ? \u003cdiv\u003e{user.name}\u003c/div\u003e : \u003cdiv\u003eLoading...\u003c/div\u003e;\n}\n```\n\n---\n\n### useScript\n\nHook to dynamically load external scripts with status tracking and callback support.\n\n**Parameters:**\n- `url: string` - The URL of the script to load\n- `options?: UseScriptOptions` - Optional configuration:\n  - `onLoad?: () =\u003e void` - Callback when script loads successfully\n  - `onError?: () =\u003e void` - Callback when script fails to load\n  - `removeOnUnmount?: boolean` - Whether to remove script on unmount (default: `true`)\n\n**Returns:**\n- `UseScriptStatus` - The current status: `'idle' | 'loading' | 'ready' | 'error'`\n\n**Side effects:**\n- Appends a `\u003cscript\u003e` tag to `document.body`\n- Removes the script tag on unmount (unless `removeOnUnmount: false`)\n- Detects and reuses already-loaded scripts\n\n**Example:**\n```tsx\nimport { useScript } from 'react-essentials-functions';\n\nfunction GoogleMapsComponent() {\n  const status = useScript('https://maps.googleapis.com/maps/api/js', {\n    onLoad: () =\u003e console.log('Google Maps loaded'),\n    onError: () =\u003e console.error('Failed to load Google Maps'),\n  });\n\n  if (status === 'loading') return \u003cdiv\u003eLoading map...\u003c/div\u003e;\n  if (status === 'error') return \u003cdiv\u003eError loading map\u003c/div\u003e;\n  if (status === 'idle') return \u003cdiv\u003eInitializing...\u003c/div\u003e;\n\n  return \u003cdiv\u003eMap is ready!\u003c/div\u003e;\n}\n```\n\n---\n\n### useSessionStorage\n\nHook that syncs state with `sessionStorage`. Handles JSON serialization/deserialization automatically. Falls back gracefully when `sessionStorage` is unavailable (SSR, private browsing). Unlike `useLocalStorage`, data persists only within the current browser tab and is cleared when the tab is closed.\n\n**Type Parameters:**\n- `T` - The type of the stored value\n\n**Parameters:**\n- `key: string` - The sessionStorage key\n- `initialValue: T` - The initial value if nothing is stored\n\n**Returns:**\n- `[T, (value: T | ((prev: T) =\u003e T)) =\u003e void, () =\u003e void]` - A tuple containing:\n  - The current stored value\n  - A setter function (accepts value or updater function)\n  - A remove function to clear the key from sessionStorage\n\n**Side effects:**\n- Reads from `sessionStorage` on initialization\n- Writes to `sessionStorage` on every value change\n- `removeValue` deletes the key from `sessionStorage`\n\n**Example:**\n```tsx\nimport { useSessionStorage } from 'react-essentials-functions';\n\nfunction MultiStepForm() {\n  const [step, setStep, resetStep] = useSessionStorage('form-step', 0);\n  const [formData, setFormData, clearFormData] = useSessionStorage('form-data', {\n    name: '',\n    email: '',\n  });\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eStep {step + 1} of 3\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setStep(prev =\u003e prev + 1)}\u003eNext\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e { resetStep(); clearFormData(); }}\u003e\n        Start over\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useSet\n\nHook for managing a `Set` as React state. Provides convenient methods to add, remove, and toggle values.\n\n**Type Parameters:**\n- `T` - The value type\n\n**Parameters:**\n- `initialValues?: Iterable\u003cT\u003e` - Optional initial values for the Set\n\n**Returns:**\n- `UseSetReturn\u003cT\u003e` - Object containing:\n  - `set: Set\u003cT\u003e` - The current Set\n  - `add: (value: T) =\u003e void` - Add a value\n  - `remove: (value: T) =\u003e void` - Remove a value\n  - `toggle: (value: T) =\u003e void` - Toggle a value (add if absent, remove if present)\n  - `has: (value: T) =\u003e boolean` - Check if a value exists\n  - `clear: () =\u003e void` - Clear all values\n  - `reset: () =\u003e void` - Reset to initial values\n  - `size: number` - Number of values\n\n**Example:**\n```tsx\nimport { useSet } from 'react-essentials-functions';\n\nfunction TagSelector() {\n  const { set, toggle, has } = useSet\u003cstring\u003e(['react']);\n\n  const tags = ['react', 'typescript', 'node', 'vue'];\n\n  return (\n    \u003cdiv\u003e\n      {tags.map(tag =\u003e (\n        \u003cbutton\n          key={tag}\n          onClick={() =\u003e toggle(tag)}\n          style={{ fontWeight: has(tag) ? 'bold' : 'normal' }}\n        \u003e\n          {tag}\n        \u003c/button\u003e\n      ))}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useTheme\n\nHook to manage theme (light/dark) with `localStorage` persistence and SSR support. Automatically detects system color scheme preference via `prefers-color-scheme` when no theme has been previously stored.\n\n**Returns:**\n- `[ThemeMode, () =\u003e void, boolean]` - A tuple containing:\n  - Current theme mode (`'light' | 'dark'`)\n  - Function to toggle between themes\n  - Boolean indicating if component is mounted (useful for SSR)\n\n**Side effects:**\n- Reads/writes to `localStorage` with key `'theme'`\n- Detects system `prefers-color-scheme` preference on first load\n\n**Example:**\n```tsx\nimport { useTheme } from 'react-essentials-functions';\n\nfunction ThemeToggle() {\n  const [theme, toggleTheme, mounted] = useTheme();\n\n  // Avoid hydration mismatch\n  if (!mounted) return null;\n\n  return (\n    \u003cbutton onClick={toggleTheme}\u003e\n      Switch to {theme === 'light' ? 'dark' : 'light'} mode\n    \u003c/button\u003e\n  );\n}\n```\n\n---\n\n### useTimeout\n\nHook that sets up a declarative `setTimeout`. The timeout is automatically cleared on unmount. Pass `null` as delay to cancel the timeout. Always uses the latest callback.\n\n**Parameters:**\n- `callback: () =\u003e void` - Function to call when the timeout fires\n- `delay: number | null` - Timeout delay in milliseconds, or `null` to cancel\n\n**Returns:**\n- `void`\n\n**Side effects:**\n- Creates a `setTimeout` timer\n- Clears the timeout on unmount or when delay changes\n\n**Example:**\n```tsx\nimport { useTimeout } from 'react-essentials-functions';\nimport { useState } from 'react';\n\nfunction Toast({ message }: { message: string }) {\n  const [visible, setVisible] = useState(true);\n\n  useTimeout(() =\u003e {\n    setVisible(false);\n  }, 5000);\n\n  return visible ? \u003cdiv className=\"toast\"\u003e{message}\u003c/div\u003e : null;\n}\n```\n\n---\n\n### useToggle\n\nHook for managing a boolean toggle state. Provides a simple API for toggling, setting true, or setting false. Useful for modals, dropdowns, accordions, etc.\n\n**Parameters:**\n- `initialValue?: boolean` - The initial boolean value (default: `false`)\n\n**Returns:**\n- `[boolean, () =\u003e void, () =\u003e void, () =\u003e void]` - A tuple containing:\n  - The current boolean value\n  - `toggle` - Flips the value\n  - `setTrue` - Sets to `true`\n  - `setFalse` - Sets to `false`\n\n**Example:**\n```tsx\nimport { useToggle } from 'react-essentials-functions';\n\nfunction Modal() {\n  const [isOpen, toggleOpen, open, close] = useToggle(false);\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={open}\u003eOpen Modal\u003c/button\u003e\n      {isOpen \u0026\u0026 (\n        \u003cdiv className=\"modal\"\u003e\n          \u003cp\u003eModal content\u003c/p\u003e\n          \u003cbutton onClick={close}\u003eClose\u003c/button\u003e\n        \u003c/div\u003e\n      )}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n### useWindowDimensions\n\nHook to get the current window dimensions with automatic updates on resize. SSR-safe (returns `0` for both dimensions when `window` is unavailable).\n\n**Returns:**\n- `WindowDimensions` - Object containing `width` and `height` of the window\n\n**Side effects:**\n- Adds a `resize` event listener on `window`\n- Removes listener on unmount\n\n**Example:**\n```tsx\nimport { useWindowDimensions } from 'react-essentials-functions';\n\nfunction ResponsiveComponent() {\n  const { width, height } = useWindowDimensions();\n\n  return (\n    \u003cdiv\u003e\n      Window size: {width}px x {height}px\n      {width \u003c 768 ? \u003cMobileLayout /\u003e : \u003cDesktopLayout /\u003e}\n    \u003c/div\u003e\n  );\n}\n```\n\n---\n\n## Components\n\n### ConditionalWrapper\n\nComponent that conditionally wraps its children with a wrapper component based on a condition. When the condition is `false`, children are rendered unwrapped inside a fragment.\n\n**Props:**\n- `condition: boolean` - Whether to wrap the children\n- `wrapper: (children: React.ReactNode) =\u003e JSX.Element` - Function that returns the wrapper element\n- `children: React.ReactNode` - Children to wrap\n\n**Example:**\n```tsx\nimport { ConditionalWrapper } from 'react-essentials-functions';\n\nfunction LinkWrapper({ link, children }) {\n  return (\n    \u003cConditionalWrapper\n      condition={!!link}\n      wrapper={(c) =\u003e \u003ca href={link}\u003e{c}\u003c/a\u003e}\n    \u003e\n      \u003cbutton\u003e{children}\u003c/button\u003e\n    \u003c/ConditionalWrapper\u003e\n  );\n}\n```\n\n---\n\n## TypeScript Support\n\nThis library is written in TypeScript and includes full type definitions. All types are exported for your convenience:\n\n```ts\nimport type {\n  Dimensions,\n  WindowDimensions,\n  ThemeMode,\n  UseClipboardReturn,\n  UseCounterReturn,\n  UseHoverReturn,\n  UseIdleReturn,\n  UseIntersectionObserverOptions,\n  UseIntersectionObserverReturn,\n  UseKeyPressOptions,\n  UseMapReturn,\n  UseScriptStatus,\n  UseScriptOptions,\n  UseSetReturn,\n  ConditionalWrapperProps,\n} from 'react-essentials-functions';\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxgfr%2Freact-essentials-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxgfr%2Freact-essentials-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxgfr%2Freact-essentials-functions/lists"}