Installation

Add the package once

Install the package, then import from the React or Vue entry that matches your app.

$ npm install @cermuel/hooks

Usage

Use the framework-native entry

useOnline

import { useOnline } from "@cermuel/hooks/react";export function Status() {  const online = useOnline();  return online ? "Online" : "Offline";}

All hooks

Every hook in the package

useAsync

import { useAsync } from "@cermuel/hooks/react";export function useAsyncExample() {  async function fetchUser() { return { name: 'Ada' }; }  const request = useAsync(fetchUser);  return <button type="button" onClick={() => void request.execute()}>Load</button>;}

useAutosave

import { useState } from "react";import { useAutosave } from "@cermuel/hooks/react";export function useAutosaveExample() {  const [draft, setDraft] = useState({ title: "" });  const autosave = useAutosave(draft, async () => undefined);  return (    <label>      <input        value={draft.title}        onChange={(event) => setDraft({ title: event.target.value })}      />      {autosave.status}    </label>  );}

useBattery

import { useBattery } from "@cermuel/hooks/react";export function useBatteryExample() {  const battery = useBattery();  return <p>{battery.level == null ? "Unknown" : battery.level}</p>;}

useBreakpoint

import { useBreakpoint } from "@cermuel/hooks/react";export function useBreakpointExample() {  const breakpoint = useBreakpoint({ sm: 640, md: 768, lg: 1024 });  return <p>{breakpoint}</p>;}

useClickOutside

import { useState, useRef } from "react";import { useClickOutside } from "@cermuel/hooks/react";export function useClickOutsideExample() {  const [open, setOpen] = useState(true);  const target = useRef<HTMLDivElement>(null);  useClickOutside(target, () => setOpen(false));  return <div ref={target}>{open ? "Open" : "Closed"}</div>;}

useClipboard

import { useClipboard } from "@cermuel/hooks/react";export function useClipboardExample() {  const { text, copy } = useClipboard();  return <button type="button" onClick={() => void copy("Hello")}>{text || "Copy"}</button>;}

useCountdown

import { useCountdown } from "@cermuel/hooks/react";export function useCountdownExample() {  const countdown = useCountdown(10);  return <button type="button" onClick={countdown.start}>{countdown.remaining}</button>;}

useDebounce

import { useDebounce } from "@cermuel/hooks/react";import { useState } from "react";export function UseDebounceExample() {  const [query, setQuery] = useState("");  const debouncedQuery = useDebounce(query, 300);  return (    <label>      Search      <input value={query} onChange={(event) => setQuery(event.target.value)} />      <span>Debounced value: {debouncedQuery}</span>    </label>  );}

useDeviceOrientation

import { useDeviceOrientation } from "@cermuel/hooks/react";export function useDeviceOrientationExample() {  const orientation = useDeviceOrientation();  return <p>{orientation.alpha ?? 0}</p>;}

useDocumentVisibility

import { useDocumentVisibility } from "@cermuel/hooks/react";export function useDocumentVisibilityExample() {  const visible = useDocumentVisibility();  return <p>{visible ? "Visible" : "Hidden"}</p>;}

useDragAndDrop

import { useDragAndDrop } from "@cermuel/hooks/react";export function useDragAndDropExample() {  const drag = useDragAndDrop(['a', 'b']);  return <p>{drag.items.join(', ')}</p>;}

useElementSize

import { useElementSize } from "@cermuel/hooks/react";export function useElementSizeExample() {  const { ref, width, height } = useElementSize<HTMLDivElement>();  return <div ref={ref}>{width} x {height}</div>;}

useElementVisibility

import { useElementVisibility } from "@cermuel/hooks/react";export function useElementVisibilityExample() {  const { ref, visible } = useElementVisibility<HTMLDivElement>();  return <div ref={ref}>{visible ? "Visible" : "Hidden"}</div>;}

useEventListener

import { useState } from "react";import { useEventListener } from "@cermuel/hooks/react";export function useEventListenerExample() {  const [count, setCount] = useState(0);  useEventListener("resize", () => setCount((value) => value + 1));  return <p>Resizes: {count}</p>;}

useFileUpload

import { useFileUpload } from "@cermuel/hooks/react";export function useFileUploadExample() {  const upload = useFileUpload({ accept: ['image/png'] });  return <p>{upload.files.length} files</p>;}

useFocusTrap

import { useRef } from "react";import { useFocusTrap } from "@cermuel/hooks/react";export function useFocusTrapExample() {  const target = useRef<HTMLDivElement>(null);  useFocusTrap(target, true);  return <div ref={target} tabIndex={-1}>Dialog</div>;}

useForm

import { useForm } from "@cermuel/hooks/react";export function useFormExample() {  const form = useForm({ email: '' });  return <p>{form.dirty ? "Dirty" : "Clean"}</p>;}

useFormPersist

import { useState } from "react";import { useFormPersist } from "@cermuel/hooks/react";export function useFormPersistExample() {  const [draft, setDraft] = useState({ title: '' });  useFormPersist('contact-form', draft);  return <input value={draft.title} onChange={(event) => setDraft({ title: event.target.value })} />;}

useFullscreen

import { useFullscreen } from "@cermuel/hooks/react";export function useFullscreenExample() {  const fullscreen = useFullscreen();  return <button type="button" onClick={fullscreen.toggle}>Toggle fullscreen</button>;}

useGeolocation

import { useGeolocation } from "@cermuel/hooks/react";export function useGeolocationExample() {  const location = useGeolocation();  return <p>{location.supported ? location.permission : "Unsupported"}</p>;}

useIdle

import { useIdle } from "@cermuel/hooks/react";export function useIdleExample() {  const idle = useIdle(60000);  return <p>{idle.idle ? "Idle" : "Active"}</p>;}

useInfiniteScroll

import { useInfiniteScroll } from "@cermuel/hooks/react";export function useInfiniteScrollExample() {  async function loadMore() { return ['next']; }  const loader = useInfiniteScroll(loadMore);  return <div ref={loader.ref}>{loader.loading ? "Loading" : "Ready"}</div>;}

useInterval

import { useState } from "react";import { useInterval } from "@cermuel/hooks/react";export function useIntervalExample() {  const [ticks, setTicks] = useState(0);  const { start, stop, running } = useInterval(() => setTicks((value) => value + 1), 1000);  return <button type="button" onClick={running ? stop : start}>{running ? "Stop" : `Start ${ticks}`}</button>;}

useKeyboardShortcut

import { useState } from "react";import { useKeyboardShortcut } from "@cermuel/hooks/react";export function useKeyboardShortcutExample() {  const [open, setOpen] = useState(false);  useKeyboardShortcut("mod+k", () => setOpen(true));  return <p>{open ? "Open" : "Closed"}</p>;}

useLocalStorage

import { useLocalStorage } from "@cermuel/hooks/react";export function useLocalStorageExample() {  const [name, setName] = useLocalStorage("name", "Ada");  return <input value={name} onChange={(event) => setName(event.target.value)} />;}

useLongPress

import { useState } from "react";import { useLongPress } from "@cermuel/hooks/react";export function useLongPressExample() {  const [pressed, setPressed] = useState(false);  const press = useLongPress(() => setPressed(true));  return <button type="button" {...press.bind}>{pressed ? "Pressed" : "Hold"}</button>;}

useMediaQuery

import { useMediaQuery } from "@cermuel/hooks/react";export function useMediaQueryExample() {  const wide = useMediaQuery("(min-width: 768px)");  return <p>{wide ? "Wide" : "Compact"}</p>;}

useOnline

import { useOnline } from "@cermuel/hooks/react";export function UseOnlineExample() {  const online = useOnline();  return <p>You are currently {online ? "online" : "offline"}.</p>;}

useOtpInput

import { useOtpInput } from "@cermuel/hooks/react";export function useOtpInputExample() {  const otp = useOtpInput(6);  return <p>{otp.value}</p>;}

usePageFocus

import { usePageFocus } from "@cermuel/hooks/react";export function usePageFocusExample() {  const focused = usePageFocus();  return <p>{focused ? "Focused" : "Blurred"}</p>;}

usePagination

import { usePagination } from "@cermuel/hooks/react";export function usePaginationExample() {  const pagination = usePagination(120, { pageSize: 10 });  return <button type="button" onClick={pagination.next}>Next</button>;}

usePermission

import { usePermission } from "@cermuel/hooks/react";export function usePermissionExample() {  const camera = usePermission("camera" as PermissionName);  return <p>{camera.state}</p>;}

usePolling

import { usePolling } from "@cermuel/hooks/react";export function usePollingExample() {  async function fetchUser() { return { name: 'Ada' }; }  const polling = usePolling(fetchUser, { interval: 5000 });  return <p>{polling.running ? "Polling" : "Paused"}</p>;}

usePreferredTheme

import { usePreferredTheme } from "@cermuel/hooks/react";export function usePreferredThemeExample() {  const theme = usePreferredTheme();  return <p>{theme.theme}</p>;}

usePrevious

import { useState } from "react";import { usePrevious } from "@cermuel/hooks/react";export function usePreviousExample() {  const [count, setCount] = useState(0);  const previous = usePrevious(count);  return <button type="button" onClick={() => setCount(count + 1)}>Previous: {previous ?? 0}</button>;}

useReducedMotion

import { useReducedMotion } from "@cermuel/hooks/react";export function useReducedMotionExample() {  const reduced = useReducedMotion();  return <p>{reduced ? "Reduced" : "Animated"}</p>;}

useRetry

import { useRetry } from "@cermuel/hooks/react";export function useRetryExample() {  async function saveUser() { return true; }  const retry = useRetry(saveUser);  return <button type="button" onClick={() => void retry.run()}>Save</button>;}

useScrollDirection

import { useScrollDirection } from "@cermuel/hooks/react";export function useScrollDirectionExample() {  const direction = useScrollDirection();  return <p>{direction}</p>;}

useScrollLock

import { useScrollLock } from "@cermuel/hooks/react";export function useScrollLockExample() {  const { locked, lock, unlock } = useScrollLock();  return <button type="button" onClick={locked ? unlock : lock}>{locked ? "Unlock" : "Lock"}</button>;}

useScrollPosition

import { useScrollPosition } from "@cermuel/hooks/react";export function useScrollPositionExample() {  const position = useScrollPosition();  return <p>{position.x}, {position.y}</p>;}

useSearchParamsState

import { useSearchParamsState } from "@cermuel/hooks/react";export function useSearchParamsStateExample() {  const [query, setQuery] = useSearchParamsState("q", "");  return <input value={query} onChange={(event) => setQuery(event.target.value)} />;}

useSelection

import { useSelection } from "@cermuel/hooks/react";export function useSelectionExample() {  const selection = useSelection(['a', 'b', 'c']);  return <p>{selection.selected.size} selected</p>;}

useSessionStorage

import { useSessionStorage } from "@cermuel/hooks/react";export function useSessionStorageExample() {  const [name, setName] = useSessionStorage("name", "Ada");  return <input value={name} onChange={(event) => setName(event.target.value)} />;}

useSyncedState

import { useSyncedState } from "@cermuel/hooks/react";export function useSyncedStateExample() {  const [value, setValue] = useSyncedState("shared", "");  return <input value={value} onChange={(event) => setValue(event.target.value)} />;}

useThrottle

import { useState } from "react";import { useThrottle } from "@cermuel/hooks/react";export function useThrottleExample() {  const [search, setSearch] = useState("");  const value = useThrottle(search, 300);  return <input value={value} onChange={(event) => setSearch(event.target.value)} />;}

useUndoRedo

import { useUndoRedo } from "@cermuel/hooks/react";export function useUndoRedoExample() {  const history = useUndoRedo('draft');  return <button type="button" onClick={history.undo}>Undo</button>;}

useUnsavedChanges

import { useState } from "react";import { useUnsavedChanges } from "@cermuel/hooks/react";export function useUnsavedChangesExample() {  const [dirty, setDirty] = useState(false);  useUnsavedChanges(dirty);  return <button type="button" onClick={() => setDirty(true)}>{dirty ? "Unsaved" : "Saved"}</button>;}

useWebSocket

import { useWebSocket } from "@cermuel/hooks/react";export function useWebSocketExample() {  const socket = useWebSocket("wss://example.com");  return <p>{socket.status}</p>;}

useWindowSize

import { useWindowSize } from "@cermuel/hooks/react";export function useWindowSizeExample() {  const size = useWindowSize();  return <p>{size.width} x {size.height}</p>;}