{"id":13850313,"url":"https://github.com/charlie-tango/hooks","last_synced_at":"2025-07-12T21:33:43.363Z","repository":{"id":34227863,"uuid":"171364576","full_name":"charlie-tango/hooks","owner":"charlie-tango","description":"Collection of React Hooks used by Charlie Tango","archived":false,"fork":false,"pushed_at":"2024-08-13T14:21:37.000Z","size":2151,"stargazers_count":76,"open_issues_count":0,"forks_count":10,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-11-18T14:48:55.776Z","etag":null,"topics":["collection","hooks","react","utility"],"latest_commit_sha":null,"homepage":"","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/charlie-tango.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2019-02-18T22:19:51.000Z","updated_at":"2024-10-07T01:22:16.000Z","dependencies_parsed_at":"2024-07-29T09:16:32.995Z","dependency_job_id":"99fd456d-798a-4e47-825b-34cd36e104c4","html_url":"https://github.com/charlie-tango/hooks","commit_stats":{"total_commits":163,"total_committers":12,"mean_commits":"13.583333333333334","dds":0.2024539877300614,"last_synced_commit":"41fc8eb0903b5edc7271aa17129a3813fb434722"},"previous_names":[],"tags_count":210,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlie-tango%2Fhooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlie-tango%2Fhooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlie-tango%2Fhooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlie-tango%2Fhooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/charlie-tango","download_url":"https://codeload.github.com/charlie-tango/hooks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225839485,"owners_count":17532305,"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":["collection","hooks","react","utility"],"created_at":"2024-08-04T20:01:05.949Z","updated_at":"2025-07-12T21:33:43.344Z","avatar_url":"https://github.com/charlie-tango.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Charlie Tango Hooks\n\n[![npm version][npm-version-src]][npm-version-href]\n[![License][license-src]][license-href]\n\nCollection of React Hooks used by [Charlie Tango](https://www.charlietango.dk/).\n\n- Written in TypeScript, with full types support.\n- Small and focused, each hook does one thing well.\n- No barrel file, only import the hooks you need.\n- Exported as ESM.\n- Optimized for modern React, uses newer APIs like `useSyncExternalStore`.\n- All hooks work in a server-side rendering environment.\n- All hooks are tested with [Vitest](https://vitest.dev/) in a real browser environment.\n\n## Installation\n\nInstall using npm:\n\n```sh\nnpm install @charlietango/hooks --save\n```\n\n## The Hooks\n\nAll the hooks are exported on their own, so we don't have a barrel file with all the hooks.\nThis guarantees that you only import the hooks you need, and don't bloat your bundle with unused code.\n\n### `useCookie`\n\nA hook to interact with the `document.cookie`. It works just like the `useState` hook, but it will persist the value in the cookie.\nThe hook only sets and gets the `string` value - If you need to store an object, you need to serialize it yourself.\n\n```ts\nimport { useCookie } from \"@charlietango/hooks/use-cookie\";\n\nconst [value, setValue] = useCookie(\"mode\");\n```\n\nIf the cookies is changed outside the `useCookie` hook, you can call the `revalidateCookies`, to get React to reevaluate the cookie values.\n\n```ts\nimport { revalidateCookies } from \"@charlietango/hooks/use-cookie\";\n\nrevalidateCookies();\n```\n\n### `useDebouncedValue`\n\nDebounce a value. The value will only be updated after the delay has passed without the value changing.\n\n```ts\nimport { useDebouncedValue } from \"@charlietango/hooks/use-debounced-value\";\n\nconst [debouncedValue, setDebouncedValue] = useDebouncedValue(\n  initialValue,\n  500,\n);\n\nsetDebouncedValue(\"Hello\");\nsetDebouncedValue(\"World\");\nconsole.log(debouncedValue); // Will log \"Hello\" until 500ms has passed\n```\n\nThe `setDebouncedValue` also contains a few control methods, that can be useful:\n\n- `flush`: Call the callback immediately, and cancel debouncing.\n- `cancel`: Cancel debouncing, and the callback will never be called.\n- `isPending`: Check if the callback is waiting to be called.\n  You can use them like this:\n\n```tsx\nconst [debouncedValue, setDebouncedValue] = useDebouncedValue(\n  initialValue,\n  500,\n);\n\nsetDebouncedValue(\"Hello\");\nsetDebouncedValue.isPending(); // true\nsetDebouncedValue.flush(); // Logs \"Hello\"\nsetDebouncedValue(\"world\");\nsetDebouncedValue.cancel(); // Will never log \"world\"\n```\n\n### `useDebouncedCallback`\n\nDebounce a callback function. The callback will only be called after the delay has passed without the function being called again.\n\n```ts\nimport { useDebouncedCallback } from \"@charlietango/hooks/use-debounced-callback\";\n\nconst debouncedCallback = useDebouncedCallback((value: string) =\u003e {\n  console.log(value);\n}, 500);\n\ndebouncedCallback(\"Hello\");\ndebouncedCallback(\"World\"); // Will only log \"World\" after 500ms\n```\n\nThe `debouncedCallback` also contains a few control methods, that can be useful:\n\n- `flush`: Call the callback immediately, and cancel debouncing.\n- `cancel`: Cancel debouncing, and the callback will never be called.\n- `isPending`: Check if the callback is waiting to be called.\n\nYou can use them like this:\n\n```tsx\nconst debouncedCallback = useDebouncedCallback((value: string) =\u003e {\n  console.log(value);\n}, 500);\n\ndebouncedCallback(\"Hello\");\ndebouncedCallback.isPending(); // true\ndebouncedCallback.flush(); // Logs \"Hello\"\ndebouncedCallback(\"world\");\ndebouncedCallback.cancel(); // Will never log \"world\"\n```\n\n### `useElementSize`\n\nMonitor the size of an element, and return the size object.\nUses the ResizeObserver API, so it will keep track of the size changes.\n\n```ts\nimport { useElementSize } from \"@charlietango/hooks/use-element-size\";\n\nconst { ref, size } = useElementSize(options);\n```\n\n### `useMedia`\n\nMonitor a media query, and return a boolean indicating if the media query matches. Until the media query is matched, the hook will return `undefined`.\n\n```ts\nimport { useMedia } from \"@charlietango/hooks/use-media\";\n\nconst isDesktop = useMedia({ minWidth: 1024 });\nconst prefersReducedMotion = useMedia(\n  \"(prefers-reduced-motion: no-preference)\",\n);\n```\n\n### `usePrevious`\n\nKeep track of the previous value of a variable.\n\n```ts\nimport { usePrevious } from \"@charlietango/hooks/use-previous\";\n\nconst prevValue = usePrevious(value);\n```\n\n### `useScript`\n\nWhen loading external scripts, you might want to know when the script has loaded, and if there was an error.\nBecause it's external, it won't be able to trigger a callback when it's done - Therefor you need to monitor the `\u003cscript\u003e` tag itself.\nThe `useScript` hook will handle this for you.\n\nYou can load the same script multiple times, and the hook will share the script and status between all instances.\n\n```ts\nimport { useScript } from \"@charlietango/hooks/use-script\";\n\nconst status = useScript(\"https://example.com/script.js\"); // \"idle\" | \"loading\" | \"ready\" | \"error\"\nif (status === \"ready\") {\n  // Script is loaded\n}\n```\n\n### `useStorage`\n\nA hook to interact with the `localStorage` or `sessionStorage`. It works just like the `useState` hook, but it will persist the value in the storage.\nThe hook only sets and gets the `string` value - If you need to store an object, you need to serialize it yourself.\n\n```ts\nimport { useStorage } from \"@charlietango/hooks/use-storage\";\n\nconst [value, setValue] = useStorage(\"mode\", { mode: \"local\" });\nsetValue(\"dark\");\n```\n\n### `useWindowSize`\n\nGet the current window size. If the window resizes, the hook will update the size.\n\n```ts\nimport { useWindowSize } from \"@charlietango/hooks/use-window-size\";\n\nconst { width, height } = useWindowSize();\n```\n\n\u003c!-- Badges --\u003e\n\n[npm-version-src]: https://img.shields.io/npm/v/@charlietango/hooks?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[npm-version-href]: https://npmjs.com/package/@charlietango/hooks\n[license-src]: https://img.shields.io/github/license/charlie-tango/hooks.svg?style=flat\u0026colorA=080f12\u0026colorB=1fa669\n[license-href]: https://github.com/charlie-tango/hooks/blob/main/LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlie-tango%2Fhooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcharlie-tango%2Fhooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlie-tango%2Fhooks/lists"}