{"id":13450363,"url":"https://github.com/wellyshen/react-cool-onclickoutside","last_synced_at":"2025-04-07T23:13:19.113Z","repository":{"id":38296262,"uuid":"231929480","full_name":"wellyshen/react-cool-onclickoutside","owner":"wellyshen","description":"😎 🖱 React hook to listen for clicks outside of the component(s).","archived":false,"fork":false,"pushed_at":"2023-08-12T16:40:16.000Z","size":6470,"stargazers_count":547,"open_issues_count":30,"forks_count":13,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-22T21:14:18.596Z","etag":null,"topics":["elements","hook","onclickoutside","react","typescript"],"latest_commit_sha":null,"homepage":"https://react-cool-onclickoutside.netlify.app","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/wellyshen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null},"funding":{"github":null,"patreon":null,"open_collective":"react-cool-onclickoutside","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-01-05T14:28:07.000Z","updated_at":"2024-04-17T04:20:31.000Z","dependencies_parsed_at":"2023-09-23T14:37:19.935Z","dependency_job_id":null,"html_url":"https://github.com/wellyshen/react-cool-onclickoutside","commit_stats":{"total_commits":922,"total_committers":7,"mean_commits":"131.71428571428572","dds":0.3926247288503254,"last_synced_commit":"d00ab4a9b2f75901aa5ec52255a6a1d84398ea97"},"previous_names":[],"tags_count":57,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-onclickoutside","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-onclickoutside/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-onclickoutside/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-onclickoutside/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wellyshen","download_url":"https://codeload.github.com/wellyshen/react-cool-onclickoutside/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247334376,"owners_count":20922203,"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":["elements","hook","onclickoutside","react","typescript"],"created_at":"2024-07-31T07:00:33.965Z","updated_at":"2025-04-07T23:13:19.077Z","avatar_url":"https://github.com/wellyshen.png","language":"TypeScript","funding_links":["https://opencollective.com/react-cool-onclickoutside"],"categories":["Packages"],"sub_categories":[],"readme":"# \u003cem\u003e\u003cb\u003eREACT COOL ONCLICKOUTSIDE\u003c/b\u003e\u003c/em\u003e\n\nThis is a React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook) to trigger callback when user clicks outside of the target component(s) area. It's a useful logic for UI interaction design (IxD) like dismiss a dropdown menu, modal or tooltip etc. You can check the [features](#features) section to learn more.\n\n⚡️ Live demo: https://react-cool-onclickoutside.netlify.app\n\n❤️ it? ⭐️ it on [GitHub](https://github.com/wellyshen/react-cool-onclickoutside/stargazers) or [Tweet](https://twitter.com/intent/tweet?text=With%20@react-cool-onclickoutside,%20I%20can%20build%20UI%20components%20efficiently.%20Thanks,%20@Welly%20Shen%20🤩) about it.\n\n[![build status](https://img.shields.io/github/workflow/status/wellyshen/react-cool-onclickoutside/CI?style=flat-square)](https://github.com/wellyshen/react-cool-onclickoutside/actions?query=workflow%3ACI)\n[![coverage status](https://img.shields.io/coveralls/github/wellyshen/react-cool-onclickoutside?style=flat-square)](https://coveralls.io/github/wellyshen/react-cool-onclickoutside?branch=master)\n[![npm version](https://img.shields.io/npm/v/react-cool-onclickoutside?style=flat-square)](https://www.npmjs.com/package/react-cool-onclickoutside)\n[![npm downloads](https://img.shields.io/npm/dm/react-cool-onclickoutside?style=flat-square)](https://www.npmtrends.com/react-cool-onclickoutside)\n[![npm downloads](https://img.shields.io/npm/dt/react-cool-onclickoutside?style=flat-square)](https://www.npmtrends.com/react-cool-onclickoutside)\n[![gzip size](https://badgen.net/bundlephobia/minzip/react-cool-onclickoutside?label=gzip%20size\u0026style=flat-square)](https://bundlephobia.com/result?p=react-cool-onclickoutside)\n[![All Contributors](https://img.shields.io/badge/all_contributors-4-orange?style=flat-square)](#contributors-)\n[![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square)](CONTRIBUTING.md)\n[![Twitter URL](https://img.shields.io/twitter/url?style=social\u0026url=https%3A%2F%2Fgithub.com%2Fwellyshen%2Freact-cool-onclickoutside)](https://twitter.com/intent/tweet?text=With%20@react-cool-onclickoutside,%20I%20can%20build%20UI%20components%20efficiently.%20Thanks,%20@Welly%20Shen%20🤩)\n\n## Features\n\n- 🎣 Listens for clicks outside based on React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook).\n- 👯‍♀️ Supports multiple [refs](https://reactjs.org/docs/refs-and-the-dom.html) to cover more use cases.\n- 🧻 Uses [passive event listeners](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners) to improve scrolling performance.\n- ⛔ Scrollbar can be excluded from the callback of outside clicks.\n- 🙈 [Ignores certain elements](#ignore-elements-by-css-class-name) during the event loop.\n- 🙉 Enables you to [stop listening for outside clicks](#disabling-the-event-listener) when needed.\n- 🪟 [Detects iframe clicks](#detecting-iframe-clicks) for better DX.\n- 🔩 Supports custom `refs` for [some reasons](#use-your-own-ref).\n- 📜 Supports [TypeScript](https://www.typescriptlang.org) type definition.\n- 🗄️ Server-side rendering compatibility.\n- 🦔 Tiny size ([\u003c 1kB gzipped](https://bundlephobia.com/result?p=react-cool-onclickoutside)). No external dependencies, aside for the `react`.\n\n## Requirement\n\nTo use `react-cool-onclickoutside`, you must use `react@16.8.0` or greater which includes hooks.\n\n## Installation\n\nThis package is distributed via [npm](https://www.npmjs.com/package/react-cool-onclickoutside).\n\n```sh\n$ yarn add react-cool-onclickoutside\n# or\n$ npm install --save react-cool-onclickoutside\n```\n\n## Usage\n\nCommon use case.\n\n```js\nimport { useState } from \"react\";\nimport useOnclickOutside from \"react-cool-onclickoutside\";\n\nconst Dropdown = () =\u003e {\n  const [openMenu, setOpenMenu] = useState(false);\n  const ref = useOnclickOutside(() =\u003e {\n    setOpenMenu(false);\n  });\n\n  const handleClickBtn = () =\u003e {\n    setOpenMenu(!openMenu);\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={handleClickBtn}\u003eButton\u003c/button\u003e\n      {openMenu \u0026\u0026 \u003cdiv ref={ref}\u003eMenu\u003c/div\u003e}\n    \u003c/div\u003e\n  );\n};\n```\n\n[![Edit useOnclickOutside demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/useonclickoutside-demo-g185l?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\nSupport multiple refs. Callback only be triggered when user clicks outside of the registered components.\n\n```js\nimport { useState } from \"react\";\nimport useOnclickOutside from \"react-cool-onclickoutside\";\n\nconst App = () =\u003e {\n  const [showTips, setShowTips] = useState(true);\n  const ref = useOnclickOutside(() =\u003e {\n    setShowTips(false);\n  });\n\n  return (\n    \u003cdiv\u003e\n      {showTips \u0026\u0026 (\n        \u003c\u003e\n          \u003cdiv ref={ref}\u003eTooltip 1\u003c/div\u003e\n          \u003cdiv ref={ref}\u003eTooltip 2\u003c/div\u003e\n        \u003c/\u003e\n      )}\n    \u003c/div\u003e\n  );\n};\n```\n\n## Ignore Elements by CSS Class Name\n\nYou can tell `react-cool-onclickoutside` to ignore certain elements during the event loop by the `ignore-onclickoutside` CSS class name. If you want explicit control over the class name, use the `ignoreClass` option.\n\n```js\nimport { useState } from \"react\";\nimport useOnclickOutside from \"react-cool-onclickoutside\";\n\n// Use the default CSS class name\nconst App = () =\u003e {\n  const ref = useOnclickOutside(() =\u003e {\n    // Do something...\n  });\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv ref={ref}\u003eI'm a 🍕\u003c/div\u003e\n      \u003cdiv\u003eClick me will trigger the event's callback\u003c/div\u003e\n      \u003cdiv className=\"ignore-onclickoutside\"\u003e\n        Click me won't trigger the event's callback\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\n// Use your own CSS class name\nconst App = () =\u003e {\n  const ref = useOnclickOutside(\n    () =\u003e {\n      // Do something...\n    },\n    {\n      ignoreClass: \"my-ignore-class\", // Or [\"class-1\", \"class-2\"]\n    }\n  );\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv ref={ref}\u003eI'm a 🍕\u003c/div\u003e\n      \u003cdiv\u003eClick me will trigger the event's callback\u003c/div\u003e\n      \u003cdiv className=\"my-ignore-class\"\u003e\n        Click me won't trigger the event's callback\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Disabling the Event Listener\n\nIn case you want to disable the event listener for performance reasons or fulfill some use cases. We provide the `disabled` option for you. Once you set it to `true`, the callback won’t be triggered.\n\n```js\nimport { useState } from \"react\";\nimport useOnclickOutside from \"react-cool-onclickoutside\";\n\nconst App = () =\u003e {\n  const [disabled, setDisabled] = useState(false);\n  const ref = useOnclickOutside(\n    () =\u003e {\n      // Do something...\n    },\n    { disabled }\n  );\n\n  const handleBtnClick = () =\u003e {\n    setDisabled(true);\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={handleBtnClick}\u003e\n        Stop listening for outside clicks\n      \u003c/button\u003e\n      \u003cdiv ref={ref}\u003eI'm a 🍎\u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Use Your Own `ref`\n\nIn case of you had a ref already or you want to share a ref for other purposes. You can pass in the ref instead of using the one provided by this hook.\n\n```js\nconst ref = useRef();\n\nuseOnclickOutside(\n  () =\u003e {\n    // Do something...\n  },\n  { refs: [ref] }\n);\n```\n\n## Detecting Iframe Clicks\n\nClicks on an `\u003ciframe\u003e` element won't trigger `document.documentElement` listeners, because it's literally different page with different security domain. However, when clicking on an iframe moves `focus` to its content's window that triggers the main [window.blur](https://developer.mozilla.org/en-US/docs/Web/API/Window/blur_event) event. `react-cool-onclickoutside` in conjunction the `blur` event with [document.activeElement](https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/activeElement) to detect if an iframe is clicked, and execute the provided callback.\n\nThe above-mentioned workaround has its caveats:\n\n- Clicks on an iframe will only trigger the provided callback once. Subsequent clicks on iframe will not trigger the callback until focus has been moved back to main window.\n- Move focus to iframe via keyboard navigation also triggers the provided callback.\n\nFor our convenience, this feature is enabled by default. You can optionally disable it by setting the `detectIFrame` to `false` if you find it conflicting with your use-case.\n\n## API\n\n```js\nconst ref = useOnclickOutside(callback: (event: Event) =\u003e void, options?: object);\n```\n\nYou must register the `ref` and pass the `callback` to use this hook. Moreover you can access the `event` object via the callback's parameter, default will be [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) or [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent).\n\n```js\nconst callback = (event) =\u003e {\n  console.log(\"Event: \", event);\n};\n```\n\nThe `options` object contains the following keys.\n\n| Key                | Type               | Default                       | Description                                                                                             |\n| ------------------ | ------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------- |\n| `refs`             | Array              |                               | For [some reasons](#use-your-own-ref), you can pass in your own `ref(s)` instead of using the built-in. |\n| `disabled`         | boolean            | `false`                       | Enable/disable the event listener.                                                                      |\n| `eventTypes`       | Array              | `['mousedown', 'touchstart']` | Which events to listen for.                                                                             |\n| `excludeScrollbar` | boolean            | `false`                       | Whether or not to listen (ignore) to browser scrollbar clicks.                                          |\n| `ignoreClass`      | string \\| string[] | `ignore-onclickoutside`       | To ignore certain elements during the event loop by the CSS class name that you defined.                |\n| `detectIFrame`     | boolean            | `true`                        | To disable the feature of [detecting iframe clicks](#detecting-iframe-clicks).                          |\n\n## Articles / Blog Posts\n\n\u003e 💡 If you have written any blog post or article about `react-cool-onclickoutside`, please open a PR to add it here.\n\n- Featured on [React Status #172](https://react.statuscode.com/issues/172).\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://wellyshen.com\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/21308003?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eWelly\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-onclickoutside/commits?author=wellyshen\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/wellyshen/react-cool-onclickoutside/commits?author=wellyshen\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#maintenance-wellyshen\" title=\"Maintenance\"\u003e🚧\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/DmitryScaletta\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/5096735?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDmitryScaletta\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-onclickoutside/issues?q=author%3ADmitryScaletta\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/vardani\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/58708973?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003evardani\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-onclickoutside/issues?q=author%3Avardani\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/cherepanov\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/876145?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAlexey Cherepanov\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-onclickoutside/commits?author=cherepanov\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwellyshen%2Freact-cool-onclickoutside","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwellyshen%2Freact-cool-onclickoutside","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwellyshen%2Freact-cool-onclickoutside/lists"}