{"id":13632484,"url":"https://github.com/wellyshen/react-cool-inview","last_synced_at":"2025-04-13T01:59:30.689Z","repository":{"id":37070086,"uuid":"252514002","full_name":"wellyshen/react-cool-inview","owner":"wellyshen","description":"😎 🖥️ React hook to monitor an element enters or leaves the viewport (or another element).","archived":false,"fork":false,"pushed_at":"2023-08-12T16:39:11.000Z","size":4800,"stargazers_count":1467,"open_issues_count":18,"forks_count":41,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-13T01:59:23.807Z","etag":null,"topics":["animations","component","hook","impressions","intersection-observer","inview","lazy-loading","monitor","performance","react","scroll-direction","scrolling","typescript","viewport","visibility"],"latest_commit_sha":null,"homepage":"https://react-cool-inview.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-inview","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-04-02T16:51:48.000Z","updated_at":"2025-04-01T17:00:03.000Z","dependencies_parsed_at":"2023-09-23T14:37:50.212Z","dependency_job_id":null,"html_url":"https://github.com/wellyshen/react-cool-inview","commit_stats":{"total_commits":925,"total_committers":10,"mean_commits":92.5,"dds":0.5016216216216216,"last_synced_commit":"787c60b7ab0f6b48b49db7875160de21d80b6040"},"previous_names":[],"tags_count":88,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-inview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-inview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-inview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wellyshen%2Freact-cool-inview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wellyshen","download_url":"https://codeload.github.com/wellyshen/react-cool-inview/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248654046,"owners_count":21140235,"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":["animations","component","hook","impressions","intersection-observer","inview","lazy-loading","monitor","performance","react","scroll-direction","scrolling","typescript","viewport","visibility"],"created_at":"2024-08-01T22:03:04.530Z","updated_at":"2025-04-13T01:59:30.657Z","avatar_url":"https://github.com/wellyshen.png","language":"TypeScript","funding_links":["https://opencollective.com/react-cool-inview"],"categories":["TypeScript"],"sub_categories":[],"readme":"# \u003cem\u003e\u003cb\u003eREACT COOL INVIEW\u003c/b\u003e\u003c/em\u003e\n\nA React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook) / component API that monitors an element enters or leaves the viewport (or another element) with highly-performant way, using [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). It's lightweight and super flexible, which can cover all the cases that you need, like [lazy-loading images](#lazy-loading-images) and videos, [infinite scroll](#infinite-scroll) web app, [triggering animations](#trigger-animations), [tracking impressions](#track-impressions), and more. Try it you will 👍🏻 it!\n\n❤️ it? ⭐️ it on [GitHub](https://github.com/wellyshen/react-cool-inview/stargazers) or [Tweet](https://twitter.com/intent/tweet?text=With%20@react-cool-inview,%20I%20can%20build%20a%20performant%20web%20app.%20Thanks,%20@Welly%20Shen%20🤩) about it.\n\n[![build status](https://img.shields.io/github/workflow/status/wellyshen/react-cool-inview/CI?style=flat-square)](https://github.com/wellyshen/react-cool-inview/actions?query=workflow%3ACI)\n[![coverage status](https://img.shields.io/coveralls/github/wellyshen/react-cool-inview?style=flat-square)](https://coveralls.io/github/wellyshen/react-cool-inview?branch=master)\n[![npm version](https://img.shields.io/npm/v/react-cool-inview?style=flat-square)](https://www.npmjs.com/package/react-cool-inview)\n[![npm downloads](https://img.shields.io/npm/dm/react-cool-inview?style=flat-square)](https://www.npmtrends.com/react-cool-inview)\n[![npm downloads](https://img.shields.io/npm/dt/react-cool-inview?style=flat-square)](https://www.npmtrends.com/react-cool-inview)\n[![gzip size](https://badgen.net/bundlephobia/minzip/react-cool-inview?label=gzip%20size\u0026style=flat-square)](https://bundlephobia.com/result?p=react-cool-inview)\n[![All Contributors](https://img.shields.io/badge/all_contributors-7-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-inview)](https://twitter.com/intent/tweet?text=With%20@react-cool-inview,%20I%20can%20build%20a%20performant%20web%20app.%20Thanks,%20@Welly%20Shen%20🤩)\n\n![demo](https://user-images.githubusercontent.com/21308003/91034314-6af12600-e637-11ea-8a78-76c77ecaea51.gif)\n\n⚡️ Try yourself: https://react-cool-inview.netlify.app\n\n## Features\n\n- 🚀 Monitors elements with highly-performant and non-main-thread blocking way, using [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n- 🎣 Easy to use, based on React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook) / [component](#using-as-a-component).\n- 🎛 Super flexible [API](#api) design which can cover [all the cases](#usage) that you need.\n- 🖱️ Supports [scroll direction](#scrolling-direction), cool right?\n- ✌🏻 Supports [Intersection Observer v2](#intersection-observer-v2).\n- 📜 Supports [TypeScript](#working-in-typescript) type definition.\n- 🗄️ Server-side rendering compatibility.\n- 🦔 Tiny size ([~ 1.2kB gzipped](https://bundlephobia.com/result?p=react-cool-inview)). No external dependencies, aside for the `react`.\n\n## Requirement\n\nTo use `react-cool-inview`, 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-inview).\n\n```sh\n$ yarn add react-cool-inview\n# or\n$ npm install --save react-cool-inview\n```\n\n## Usage\n\n`react-cool-inview` has a flexible [API](#api) design, it can cover simple to complex use cases for you. Here are some ideas for how you can use it.\n\n\u003e ⚠️ [Most modern browsers support Intersection Observer natively](https://caniuse.com/#feat=intersectionobserver). You can also [add polyfill](#intersection-observer-polyfill) for full browser support.\n\n### Basic usage\n\nTo monitor an element enters or leaves the viewport by the `inView` state and useful sugar events.\n\n```js\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  const { observe, unobserve, inView, scrollDirection, entry } = useInView({\n    threshold: 0.25, // Default is 0\n    onChange: ({ inView, scrollDirection, entry, observe, unobserve }) =\u003e {\n      // Triggered whenever the target meets a threshold, e.g. [0.25, 0.5, ...]\n\n      unobserve(); // To stop observing the current target element\n      observe(); // To re-start observing the current target element\n    },\n    onEnter: ({ scrollDirection, entry, observe, unobserve }) =\u003e {\n      // Triggered when the target enters the viewport\n    },\n    onLeave: ({ scrollDirection, entry, observe, unobserve }) =\u003e {\n      // Triggered when the target leaves the viewport\n    },\n    // More useful options...\n  });\n\n  return \u003cdiv ref={observe}\u003e{inView ? \"Hello, I am 🤗\" : \"Bye, I am 😴\"}\u003c/div\u003e;\n};\n```\n\n\u003e 💡 You don't have to call `unobserve` when the component is unmounted, this hook will handle it for you.\n\n### Using as a Component\n\nChanges `HelloText` when it enters the viewport. The [options](#parameter) can be passed through the props.\n\n```js\nimport { InView } from \"react-cool-inview\";\n\nconst HelloText = ({ inView, observe }) =\u003e (\n  \u003cdiv ref={observe}\u003e{inView ? \"Hello, I am 🤗\" : \"Bye, I am 😴\"}\u003c/div\u003e\n);\n\nconst App = () =\u003e (\n  \u003cInView unobserveOnEnter\u003e\n    \u003cHelloText /\u003e\n  \u003c/InView\u003e\n);\n```\n\n\u003e 💡 `InView` passes `observe` and other [props](#return-object) to the `HelloText`.\n\n### Lazy-loading Images\n\nIt's super easy to build an image lazy-loading component with `react-cool-inview` to boost the performance of your web app.\n\n```js\nimport { useInView } from \"react-cool-inview\";\n\nconst LazyImage = ({ width, height, ...rest }) =\u003e {\n  const { observe, inView } = useInView({\n    // Stop observe when the target enters the viewport, so the \"inView\" only triggered once\n    unobserveOnEnter: true,\n    // For better UX, we can grow the root margin so the image will be loaded before it comes to the viewport\n    rootMargin: \"50px\",\n  });\n\n  return (\n    \u003cdiv className=\"placeholder\" style={{ width, height }} ref={observe}\u003e\n      {inView \u0026\u0026 \u003cimg {...rest} /\u003e}\n    \u003c/div\u003e\n  );\n};\n```\n\n\u003e 💡 Looking for a comprehensive image component? Try [react-cool-img](https://github.com/wellyshen/react-cool-img), it's my other component library.\n\n### Infinite Scroll\n\nInfinite scroll is a popular design technique like Facebook and Twitter feed etc., new content being loaded as you scroll down a page. The basic concept as below.\n\n```js\nimport { useState } from \"react\";\nimport { useInView } from \"react-cool-inview\";\nimport axios from \"axios\";\n\nconst App = () =\u003e {\n  const [todos, setTodos] = useState([\"todo-1\", \"todo-2\", \"...\"]);\n  const { observe } = useInView({\n    // For better UX, we can grow the root margin so the data will be loaded earlier\n    rootMargin: \"50px 0px\",\n    // When the last item comes to the viewport\n    onEnter: ({ unobserve }) =\u003e {\n      // Pause observe when loading data\n      unobserve();\n      // Load more data\n      axios.get(\"/todos\").then((res) =\u003e {\n        setTodos([...todos, ...res.todos]);\n      });\n    },\n  });\n\n  return (\n    \u003cdiv\u003e\n      {todos.map((todo, idx) =\u003e (\n        \u003cdiv ref={idx === todos.length - 1 ? observe : null}\u003e{todo}\u003c/div\u003e\n      ))}\n    \u003c/div\u003e\n  );\n};\n```\n\n\u003e 💡 Compare to pagination, infinite scroll provides a seamless experience for users and it’s easy to see the appeal. But when it comes to render a large lists, performance will be a problem. But don't worry, [react-cool-virtual](https://github.com/wellyshen/react-cool-virtual) can help you out!\n\n### Trigger Animations\n\nAnother great use case is to trigger CSS animations once they are visible to the users.\n\n```js\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  const { observe, inView } = useInView({\n    // Stop observe when the target enters the viewport, so the \"inView\" only triggered once\n    unobserveOnEnter: true,\n    // Shrink the root margin, so the animation will be triggered once the target reach a fixed amount of visible\n    rootMargin: \"-100px 0px\",\n  });\n\n  return (\n    \u003cdiv className=\"container\" ref={observe}\u003e\n      \u003cdiv className={inView ? \"fade-in\" : \"\"}\u003eI'm a 🍟\u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### Track Impressions\n\n`react-cool-inview` can also play as an impression tracker, helps you fire an analytic event when a user sees an element or advertisement.\n\n```js\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  const { observe } = useInView({\n    // For an element to be considered \"seen\", we'll say it must be 100% in the viewport\n    threshold: 1,\n    onEnter: ({ unobserve }) =\u003e {\n      // Stop observe when the target enters the viewport, so the callback only triggered once\n      unobserve();\n      // Fire an analytic event to your tracking service\n      someTrackingService.send(\"🍋 is seen\");\n    },\n  });\n\n  return \u003cdiv ref={observe}\u003eI'm a 🍋\u003c/div\u003e;\n};\n```\n\n## Scrolling Direction\n\n`react-cool-inview` not only monitors an element enters or leaves the viewport but also tells you its scroll direction by the `scrollDirection` object. The object contains vertical (y-axios) and horizontal (x-axios) properties, they're calculated whenever the target element meets a `threshold`. If there's no enough condition for calculating, the value of the properties will be `undefined`. In addition, the value of the properties will sync with the scrolling direction of the viewport.\n\n```js\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  const {\n    observe,\n    inView,\n    // vertical will be \"up\" or \"down\", horizontal will be \"left\" or \"right\"\n    scrollDirection: { vertical, horizontal },\n  } = useInView({\n    // Scroll direction is calculated whenever the target meets a threshold\n    // more trigger points the calculation will be more instant and accurate\n    threshold: [0.2, 0.4, 0.6, 0.8, 1],\n    onChange: ({ scrollDirection }) =\u003e {\n      // We can also access the scroll direction from the event object\n      console.log(\"Scroll direction: \", scrollDirection.vertical);\n    },\n  });\n\n  return (\n    \u003cdiv ref={observe}\u003e\n      \u003cdiv\u003e{inView ? \"Hello, I am 🤗\" : \"Bye, I am 😴\"}\u003c/div\u003e\n      \u003cdiv\u003e{`You're scrolling ${vertical === \"up\" ? \"⬆️\" : \"⬇️\"}`}\u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nIf you jump to a section by the [Element.scrollTop](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop) and encounter the wrong value of the `scrollDirection`. You can use `updatePosition` method to correct the behavior.\n\n```js\nimport { useEffect } from \"react\";\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  const { observe, scrollDirection, updatePosition } = useInView({\n    threshold: [0.2, 0.4, 0.6, 0.8, 1],\n  });\n\n  useEffect(() =\u003e {\n    window.scrollTo(0, 500);\n    updatePosition(); // Ensure the target element's position has been updated after the \"window.scrollTo\"\n  }, []);\n\n  return (\n    \u003cdiv ref={observe}\u003e\n      \u003cdiv\u003e{`You're scrolling ${\n        scrollDirection.vertical === \"up\" ? \"⬆️\" : \"⬇️\"\n      }`}\u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Intersection Observer v2\n\nThe Intersection Observer v1 can perfectly tell you when an element is scrolled into the viewport, but it doesn't tell you whether the element is covered by something else on the page or whether the element has any visual effects applied to it (like `transform`, `opacity`, `filter` etc.) that can make it invisible. The main concern that has surfaced is how this kind of knowledge could be helpful in preventing [clickjacking](https://en.wikipedia.org/wiki/Clickjacking) and UI redress attacks (read this [article](https://developers.google.com/web/updates/2019/02/intersectionobserver-v2) to learn more).\n\nIf you want to track the click-through rate (CTR) or impression of an element, which is actually visible to a user, [Intersection Observer v2](https://developers.google.com/web/updates/2019/02/intersectionobserver-v2) can be the savior. Which introduces a new boolean field named [isVisible](https://w3c.github.io/IntersectionObserver/v2/#dom-intersectionobserverentry-isvisible). A `true` value guarantees that an element is visible on the page and has no visual effects applied on it. A `false` value is just the opposite. The characteristic of the `isVisible` is integrated with the `inView` state and related events (like onEnter, onLeave etc.) to provide a better DX for you.\n\nWhen using the v2, there're somethings we need to know:\n\n- Check [browser compatibility](https://caniuse.com/#feat=intersectionobserver-v2). If a browser doesn't support the v2, we will fallback to the v1 behavior.\n- Understand [how visibility is calculated](https://w3c.github.io/IntersectionObserver/v2/#calculate-visibility-algo).\n- Visibility is much more expensive to compute than intersection, only use it when needed.\n\nTo use Intersection Observer v2, we must set the `trackVisibility` and `delay` options.\n\n```js\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  // With Intersection Observer v2, the \"inView\" not only tells you the target\n  // is intersecting with the root, but also guarantees it's visible on the page\n  const { observe, inView } = useInView({\n    // Track the actual visibility of the target\n    trackVisibility: true,\n    // Set a minimum delay between notifications, it must be set to 100 (ms) or greater\n    // For performance perspective, use the largest tolerable value as much as possible\n    delay: 100,\n    onEnter: () =\u003e {\n      // Triggered when the target is visible and enters the viewport\n    },\n    onLeave: () =\u003e {\n      // Triggered when the target is visible and leaves the viewport\n    },\n  });\n\n  return \u003cdiv ref={observe}\u003e{inView ? \"Hello, I am 🤗\" : \"Bye, I am 😴\"}\u003c/div\u003e;\n};\n```\n\n## How to Share A `ref`?\n\nYou can share a `ref` as follows:\n\n```js\nimport { useRef } from \"react\";\nimport { useInView } from \"react-cool-inview\";\n\nconst App = () =\u003e {\n  const ref = useRef();\n  const { observe } = useInView();\n\n  return (\n    \u003cdiv\n      ref={(el) =\u003e {\n        observe(el); // Set the target element for monitoring\n        ref.current = el; // Share the element for other purposes\n      }}\n    /\u003e\n  );\n};\n```\n\n## Working in TypeScript\n\nThis hook supports [TypeScript](https://www.typescriptlang.org), you can tell the hook what type of element you are going to observe through the [generic type](https://www.typescriptlang.org/docs/handbook/generics.html):\n\n```ts\nconst App = () =\u003e {\n  const { observe } = useInView\u003cHTMLDivElement\u003e();\n\n  return \u003cdiv ref={observe} /\u003e;\n};\n```\n\n\u003e 💡 For more available types, please [check it out](src/react-cool-inview.d.ts).\n\n## API\n\n```js\nconst returnObj = useInView(options?: object);\n```\n\n### Return object\n\nIt's returned with the following properties.\n\n| Key               | Type     | Default | Description                                                                                                                                                                                                                                                                                                                                                                                       |\n| ----------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `observe`         | function |         | To set a target element for monitoring or re-start observing the current target element.                                                                                                                                                                                                                                                                                                          |\n| `unobserve`       | function |         | To stop observing the current target element.                                                                                                                                                                                                                                                                                                                                                     |\n| `inView`          | boolean  |         | The visible state of the target element. If it's `true`, the target element has become at least as visible as the threshold that was passed. If it's `false`, the target element is no longer as visible as the given threshold. Supports [Intersection Observer v2](#intersection-observer-v2).                                                                                                  |\n| `scrollDirection` | object   |         | The scroll direction of the target element. Which contains `vertical` and `horizontal` properties. See [scroll direction](#scrolling-direction) for more information.                                                                                                                                                                                                                             |\n| `entry`           | object   |         | The [IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) of the target element. Which may contain the [isVisible](https://w3c.github.io/IntersectionObserver/v2/#dom-intersectionobserverentry-isvisible) property of the Intersection Observer v2, depends on the [browser compatibility](https://caniuse.com/#feat=intersectionobserver-v2). |\n| `updatePosition`  | function |         | To update the current position of the target element for [some cases](#scrolling-direction).                                                                                                                                                                                                                                                                                                      |\n\n### Parameter\n\nThe `options` provides the following configurations and event callbacks for you.\n\n| Key                | Type               | Default  | Description                                                                                                                                                                                                                                                                                                                                              |\n| ------------------ | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `root`             | HTMLElement        | `window` | The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if `null`.                                                                                                                                                                      |\n| `rootMargin`       | string             | `0px`    | Margin around the root. Can have values similar to the CSS [margin](https://developer.mozilla.org/en-US/docs/Web/CSS/margin`) property, e.g. `\"10px 20px 30px 40px\"` (top, right, bottom, left). The values can be percentages. This set of values serves to grow or shrink each side of the root element's bounding box before computing intersections. |\n| `threshold`        | number \\| number[] | `0`      | Indicates at what percentage of the target's visibility the observer's callback should be executed. If you only want to detect when visibility passes the 50% mark, you can use a value of 0.5. If you want the callback to run every time visibility passes another 25%, you would specify the array [0, 0.25, 0.5, 0.75, 1].                           |\n| `trackVisibility`  | boolean            | `false`  | Indicates whether the intersection observer will track changes in a target’s [visibility](https://w3c.github.io/IntersectionObserver/v2/#visibility). It's required when [using Intersection Observer v2](#intersection-observer-v2).                                                                                                                    |\n| `delay`            | number             |          | Indicates the minimum delay in milliseconds between notifications from the intersection observer for a given target. It's required when [using Intersection Observer v2](#intersection-observer-v2).                                                                                                                                                     |\n| `unobserveOnEnter` | boolean            | `false`  | Stops observe once the target element intersects with the intersection observer's root. It's useful when you only want to trigger the hook once, e.g. [scrolling to run animations](#trigger-animations).                                                                                                                                                |\n| `onChange`         | function           |          | It's invoked whenever the target element meets a threshold specified for the intersection observer. The callback receives an event object which the same with the [return object](#return-object) of the hook.                                                                                                                                           |\n| `onEnter`          | function           |          | It's invoked when the target element enters the viewport. The callback receives an event object which the same with the [return object](#return-object) of the hook except for `inView`. Supports [Intersection Observer v2](#intersection-observer-v2).                                                                                                 |\n| `onLeave`          | function           |          | It's invoked when the target element leaves the viewport. The callback receives an event object which the same with the [return object](#return-object) of the hook except for `inView`. Supports [Intersection Observer v2](#intersection-observer-v2).                                                                                                 |\n\n## `rootMargin` Not Working As Expected?\n\nIf your web app is running in an `\u003ciframe\u003e` or you have a custom `root`, the viewport won't be the current `document`. Read the [doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#The_intersection_root_and_root_margin) to understand how do **root and root margin** work.\n\n## Intersection Observer Polyfill\n\n[Intersection Observer has good support amongst browsers](https://caniuse.com/#feat=intersectionobserver), but it's not universal. You'll need to polyfill browsers that don't support it. Polyfills is something you should do consciously at the application level. Therefore `react-cool-inview` doesn't include it.\n\nYou can use W3C's [polyfill](https://www.npmjs.com/package/intersection-observer):\n\n```sh\n$ yarn add intersection-observer\n# or\n$ npm install --save intersection-observer\n```\n\nThen import it at your app's entry point:\n\n```js\nimport \"intersection-observer\";\n```\n\nOr use dynamic imports to only load the file when the polyfill is required:\n\n```js\n(async () =\u003e {\n  if (!(\"IntersectionObserver\" in window))\n    await import(\"intersection-observer\");\n})();\n```\n\n[Polyfill.io](https://polyfill.io/v3) is an alternative way to add the polyfill when needed.\n\n## Performance Issues\n\nBe aware that the callback of the `onChange` event is executed on the main thread, it should operate as quickly as possible. If any time-consuming needs to be done, use [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) or [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).\n\n```js\nonChange = (event) =\u003e requestIdleCallback(() =\u003e this.handleChange(event));\n```\n\n## Articles / Blog Posts\n\n\u003e 💡 If you have written any blog post or article about `react-cool-inview`, please open a PR to add it here.\n\n- Featured on [React Status #187](https://react.statuscode.com/issues/187).\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  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://wellyshen.com\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/21308003?v=4?s=100\" width=\"100px;\" alt=\"Welly\"/\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-inview/commits?author=wellyshen\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/wellyshen/react-cool-inview/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\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/xregitx\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/5581875?v=4?s=100\" width=\"100px;\" alt=\"Nhan Nguyen\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNhan Nguyen\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-inview/commits?author=xregitx\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://lumenstudio.dev/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/5436545?v=4?s=100\" width=\"100px;\" alt=\"Yann Pringault\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eYann Pringault\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-inview/issues?q=author%3AKerumen\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/gabalafou\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/317883?v=4?s=100\" width=\"100px;\" alt=\"gabalafou\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003egabalafou\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-inview/commits?author=gabalafou\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/victorlacorte\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/1683634?v=4?s=100\" width=\"100px;\" alt=\"Victor\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eVictor\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-inview/issues?q=author%3Avictorlacorte\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"#infra-victorlacorte\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/max-sym\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/32452032?v=4?s=100\" width=\"100px;\" alt=\"Max\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMax\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-inview/commits?author=max-sym\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/SaidMarar\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/20561190?v=4?s=100\" width=\"100px;\" alt=\"SaidMarar\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSaidMarar\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/wellyshen/react-cool-inview/commits?author=SaidMarar\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\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-inview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwellyshen%2Freact-cool-inview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwellyshen%2Freact-cool-inview/lists"}