{"id":13622909,"url":"https://github.com/researchgate/react-intersection-observer","last_synced_at":"2025-05-14T20:06:56.257Z","repository":{"id":37561546,"uuid":"99044147","full_name":"researchgate/react-intersection-observer","owner":"researchgate","description":"React component for the Intersection \u003cObserver /\u003e API","archived":false,"fork":false,"pushed_at":"2023-07-19T05:26:06.000Z","size":8609,"stargazers_count":1143,"open_issues_count":20,"forks_count":60,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-13T14:06:55.063Z","etag":null,"topics":["impressions","intersection-observer","react","scrolling","viewport"],"latest_commit_sha":null,"homepage":"https://researchgate.github.io/react-intersection-observer/","language":"JavaScript","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/researchgate.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS"}},"created_at":"2017-08-01T21:20:23.000Z","updated_at":"2025-04-08T04:34:39.000Z","dependencies_parsed_at":"2024-01-10T21:05:17.951Z","dependency_job_id":null,"html_url":"https://github.com/researchgate/react-intersection-observer","commit_stats":{"total_commits":451,"total_committers":26,"mean_commits":"17.346153846153847","dds":0.5144124168514412,"last_synced_commit":"36e6fd37839c84c135a05dad9d0878a657e8897e"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Freact-intersection-observer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Freact-intersection-observer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Freact-intersection-observer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/researchgate%2Freact-intersection-observer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/researchgate","download_url":"https://codeload.github.com/researchgate/react-intersection-observer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248724639,"owners_count":21151561,"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":["impressions","intersection-observer","react","scrolling","viewport"],"created_at":"2024-08-01T21:01:25.600Z","updated_at":"2025-04-13T14:07:00.585Z","avatar_url":"https://github.com/researchgate.png","language":"JavaScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"React Intersection Observer\" src=\".github/logo.svg\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://travis-ci.com/researchgate/react-intersection-observer\"\u003e\u003cimg alt=\"Build Status\" src=\"https://travis-ci.com/researchgate/react-intersection-observer.svg?branch=master\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/researchgate/react-intersection-observer\"\u003e\u003cimg alt=\"Codecov\" src=\"https://img.shields.io/codecov/c/github/researchgate/react-intersection-observer.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@researchgate/react-intersection-observer\"\u003e\u003cimg alt=\"NPM version\" src=\"https://img.shields.io/npm/v/@researchgate/react-intersection-observer.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@researchgate/react-intersection-observer\"\u003e\u003cimg alt=\"Dowloads per week\" src=\"https://img.shields.io/npm/dw/@researchgate/react-intersection-observer.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\n\u003e Bring ReactIntersectionObserver over today, your React children will love it!\n\n**React Intersection Observer** is a **React** component, acting as a wrapper\nfor the **IntersectionObserver API**. It is fully declarative and takes care of\nall the imperative parts for you.\n\n**React Intersection Observer** is good at:\n\n- **reusing instances**: comparing the passed options\n- **performance**: chooses smartly when to re-render and when to re-observe\n- **being unopinionated**: how to handle visibility changes is left entirely up\n  to the developer\n- **being intuitive**: looks like the Native API\n\n\u003cbr\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTable of Contents\u003c/strong\u003e\u003c/summary\u003e\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Getting started](#getting-started)\n- [What does IntersectionObserver do?](#what-does-intersectionobserver-do)\n- [Why use this component?](#why-use-this-component)\n  - [No bookkeeping](#no-bookkeeping)\n  - [No extra markup](#no-extra-markup)\n  - [Easy to adopt](#easy-to-adopt)\n- [Documentation](#documentation)\n  - [Demos](#demos)\n  - [Recipes](#recipes)\n  - [Missing DOM nodes when observing](#missing-dom-nodes-when-observing)\n  - [Options](#options)\n  - [Notes](#notes)\n- [Polyfill](#polyfill)\n  - [Caveats](#caveats)\n- [**IntersectionObserver**'s Browser Support](#intersectionobservers-browser-support)\n  - [Out of the box](#out-of-the-box)\n  - [Using polyfill](#using-polyfill)\n- [Contributing](#contributing)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c/details\u003e\n\n---\n\n## Getting started\n\n```shell\nnpm install --save @researchgate/react-intersection-observer\n```\n\n\u003e :warning: **Please make sure you have the minimum node version installed** (as defined [in the package.json](https://github.com/researchgate/react-intersection-observer/blob/master/package.json#L6-L7))\n\u003e \n\u003e Otherwise you run into this build error:\n\u003e\n\u003e `The engine \"node\" is incompatible with this module. Expected version \"\u003e=10.18.1\". Got \"10.15.3\"`\n\n## Usage\n\n```jsx\nimport React from 'react';\nimport 'intersection-observer'; // optional polyfill\nimport Observer from '@researchgate/react-intersection-observer';\n\nclass ExampleComponent extends React.Component {\n  handleIntersection(event) {\n    console.log(event.isIntersecting);\n  }\n\n  render() {\n    const options = {\n      onChange: this.handleIntersection,\n      root: '#scrolling-container',\n      rootMargin: '0% 0% -25%',\n    };\n\n    return (\n      \u003cdiv id=\"scrolling-container\" style={{ overflow: 'scroll', height: 100 }}\u003e\n        \u003cObserver {...options}\u003e\n          \u003cdiv\u003eI am the target element\u003c/div\u003e\n        \u003c/Observer\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\nOptionally add the **polyfill** and make sure it's required on your\ndependendencies for unsupporting browsers:\n\n```shell\nnpm install --save intersection-observer\n```\n\n## What does IntersectionObserver do?\n\n\u003e IntersectionObservers calculate how much of a target element overlaps (or\n\u003e \"intersects with\") the visible portion of a page, also known as the browser's\n\u003e \"viewport\":\n\u003e\n\u003e [Dan Callahan](https://hacks.mozilla.org/2017/08/intersection-observer-comes-to-firefox/)\u0026nbsp;\u0026middot;\u0026nbsp;\u003ca href=\"https://creativecommons.org/licenses/by-sa/3.0/\"\u003e\u003cimg id=\"licensebutton_slim\" alt=\"Creative Commons License\" src=\"https://i.creativecommons.org/l/by-sa/3.0/80x15.png\" style=\"margin-right:10px;margin-bottom:4px; border: 0;\"\u003e\u003c/a\u003e\n\n![Graphic example](https://hacks.mozilla.org/files/2017/08/Blank-Diagram-Page-1.png)\n\n## Why use this component?\n\nThe motivation is to provide the easiest possible solution for observing\nelements that enter the viewport on your **React** codebase. It's fully\ndeclarative and all complexity is abstracted away, focusing on reusability, and\nlow memory consumption.\n\n### No bookkeeping\n\nIt's built with compatibility in mind, adhering 100% to the\n[native API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options)\nimplementation and DSL, but takes care of all the bookkeeping work for you.\n\nInstances and nodes are managed internally so that any changes to the passed\noptions or tree root reconciliation cleans up and re-observes nodes on-demand to\navoid any unexpected memory leaks.\n\n### No extra markup\n\nReactIntersectionObserver does not create any extra DOM elements, it attaches to\nthe only child you'll provide to it. This is done using `findDOMNode` to\nretrieve the first DOM node found. If your child already has an existing `ref`,\neither a callback or object (from createRef), these will be handled normally in\neither case.\n\n### Easy to adopt\n\nWhen using ReactIntersectionObserver the only required prop is the `onChange`\nfunction. Any changes to the visibility of the element will invoke this\ncallback, just like in the\n[native API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Targeting_an_element_to_be_observed) -\nyou’ll receive one `IntersectionObserverEntry` argument per change. This gives\nyou an ideal and flexible base to build upon.\n\nSome of the things you may want to use ReactIntersectionObserver for:\n\n- Determining advertisement impressions\n- Lazy loading - Images, or anything that will enter the viewport\n- Occlusion culling - Don't render an object until is close to the viewport\n  edges\n- [Sentinel Scrolling - Infinite scroller with a recycled Sentinel](https://github.com/researchgate/react-intersection-list)\n\n## Documentation\n\n### Demos\n\nFind multiple examples and usage guidelines under:\n[https://researchgate.github.io/react-intersection-observer/](https://researchgate.github.io/react-intersection-observer/)\n\n[![demo](https://github.com/researchgate/react-intersection-observer/blob/master/.github/demo.gif?raw=true)](https://researchgate.github.io/react-intersection-observer/)\n\n### Recipes\n\nRecipes are useful code snippets solutions to common problems, for example, how\nto use ReactIntersectionObserver within a\n[Higher Order Component](https://researchgate.github.io/react-intersection-observer/?selectedKind=Recipes\u0026selectedStory=Higher%20Order%20Component).\n\u003cbr\u003e Here's how to create an **element monitoring** component:\n\n```jsx\nimport React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport Observer from '@researchgate/react-intersection-observer';\n\nexport default class ViewableMonitor extends Component {\n  static propTypes = {\n    tag: PropTypes.node,\n    children: PropTypes.func.isRequired,\n  };\n\n  static defaultProps = {\n    tag: 'div',\n  };\n\n  state = {\n    isIntersecting: false,\n  };\n\n  handleChange = ({ isIntersecting }) =\u003e {\n    this.setState({ isIntersecting });\n  };\n\n  render() {\n    const { tag: Tag, children, ...rest } = this.props;\n\n    return (\n      \u003cObserver {...rest} onChange={this.handleChange}\u003e\n        \u003cTag\u003e{children(this.state.isIntersecting)}\u003c/Tag\u003e\n      \u003c/Observer\u003e\n    );\n  }\n}\n```\n\n```jsx\nimport React from 'react';\nimport ViewableMonitor from './ViewableMonitor';\n\nexport default () =\u003e (\n  \u003cViewableMonitor\u003e\n    {(isViewable) =\u003e (isViewable ? 'I am viewable' : 'I am still hiding')}\n  \u003c/ViewableMonitor\u003e\n);\n```\n\nDiscover more recipes in our [examples section](docs/README.md).\n\n### Missing DOM nodes when observing\n\nIn cases where there isn't a DOM node available to observe when rendering,\nyou'll be seeing an error logged in the console:\n\n```js\nReactIntersectionObserver: Can't find DOM node in the provided children. Make sure to render at least one DOM node in the tree.\n```\n\nThis somewhat helpful and descriptive message is supposed to help you identify\npotential problems implementing `observers` early on. If you miss the exception\nfor some reason and ends up in production (prone to happen with dynamic\nchildren), the entire tree will unmount so be sensible about placing your error\nboundaries.\n\nUltimately the way to avoid this is to either make sure you are rendering a DOM\nnode inside your `\u003cObserver\u003e`, or to disable the observer until there's one\n`\u003cObserver disabled\u003e`.\n\n### Options\n\n**root**: `HTMLElement|string` | default `window object`\n\nThe element or selector string that is used as the viewport for checking\nvisibility of the target.\n\n**rootMargin**: `string` | default `0px 0px 0px 0px`\n\nMargin around the root. Specify using units _px_ or _%_ (top, right, bottom\nleft). Can contain negative values.\n\n**threshold**: `number|Array\u003cnumber\u003e` | default: `0`\n\nIndicates at what percentage of the target's visibility the observer's callback\nshould be executed. If you only want to detect when visibility passes the 50%\nmark, you can use a value of 0.5. If you want the callback run every time\nvisibility passes another 25%, you would specify the array [0, 0.25, 0.5, 0.75,\n1].\n\n**disabled**: `boolean` | default: `false`\n\nControls whether the element should stop being observed by its\nIntersectionObserver instance. Useful for temporarily disabling the observing\nmechanism and restoring it later.\n\n**onChange** (required):\n`(entry: IntersectionObserverEntry, unobserve: () =\u003e void) =\u003e void`\n\nFunction that will be invoked whenever an observer's callback contains this\ntarget in its changes.\n\n**children**: `React.Element\u003c*\u003e|null`\n\nSingle React component or element that is used as the target (observable). As of\n`v1.0.0`, children can be null. Null children won't be observed.\n\n### Notes\n\n- According to the spec, an initial event is being fired when starting to\n  observe a non-intersecting element as well.\n  - _Edge's implementation seems to\n    [miss the initial event](https://github.com/w3c/IntersectionObserver/issues/222#issuecomment-311539591),\n    although Edge 16 behavior aligns with the spec._\n- Changes happen asynchronously, similar to the way `requestIdleCallback` works.\n- Although you can consider callbacks immediate - always below 1 second - you\n  can also get an immediate response on an element's visibility with\n  `observer.takeRecords()`.\n- The primitives `Map` an `Set` are required. You may need to include a polyfill\n  for browsers lacking ES2015 support. If you're using babel, include\n  `\"babel-polyfill\"` somewhere to your codebase.\n\n## Polyfill\n\nWhen needing the full spec's support, we highly recommend using the\n[IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill).\n\n### Caveats\n\n#### Ealier Spec\n\nEarlier preview versions of\n[Edge](https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12156111/)\nand prior to version 58 of\n[Chrome](https://bugs.chromium.org/p/chromium/issues/detail?id=713819#c8), the\nsupport for `isIntersecting` was lacking. This property was added to the spec\nlater and both teams where unable to implement it earlier.\n\n#### Performance issues\n\nAs the above-mentioned polyfill doesn't perform callback invocation\n[asynchronously](https://github.com/WICG/IntersectionObserver/issues/225), you\nmight want to decorate your `onChange` callback with a `requestIdleCallback` or\n`setTimeout` call to avoid a potential performance degradation:\n\n```js\nonChange = (entry) =\u003e requestIdleCallback(() =\u003e this.handleChange(entry));\n```\n\n## [**IntersectionObserver**'s Browser Support](https://platform-status.mozilla.org/)\n\n### Out of the box\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eChrome\u003c/td\u003e\n        \u003ctd\u003e51 \u003csup\u003e[1]\u003c/sup\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eFirefox (Gecko)\u003c/td\u003e\n        \u003ctd\u003e55 \u003csup\u003e[2]\u003c/sup\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eMS Edge\u003c/td\u003e\n        \u003ctd\u003e15\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eInternet Explorer\u003c/td\u003e\n        \u003ctd\u003eNot supported\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eOpera \u003csup\u003e[1]\u003c/sup\u003e\u003c/td\u003e\n        \u003ctd\u003e38\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eSafari\u003c/td\u003e\n        \u003ctd\u003e12.1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eChrome for Android\u003c/td\u003e\n        \u003ctd\u003e59\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eAndroid Browser\u003c/td\u003e\n        \u003ctd\u003e56\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eOpera Mobile\u003c/td\u003e\n        \u003ctd\u003e37\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\n- [1][reportedly available](https://www.chromestatus.com/features/5695342691483648),\n  it didn't trigger the events on initial load and lacks `isIntersecting` until\n  later versions.\n- [2] This feature was implemented in Gecko 53.0 (Firefox 53.0 / Thunderbird\n  53.0 / SeaMonkey 2.50) behind the preference\n  `dom.IntersectionObserver.enabled`.\n\n### Using polyfill\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eSafari\u003c/td\u003e\n    \u003ctd\u003e6+\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eInternet Explorer\u003c/td\u003e\n    \u003ctd\u003e7+\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eAndroid\u003c/td\u003e\n    \u003ctd\u003e4.4+\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Contributing\n\nWe'd love your help on creating React Intersection Observer!\n\nBefore you do, please read our [Code of Conduct](.github/CODE_OF_CONDUCT.md) so\nyou know what we expect when you contribute to our projects.\n\nOur [Contributing Guide](.github/CONTRIBUTING.md) tells you about our\ndevelopment process and what we're looking for, gives you instructions on how to\nissue bugs and suggest features, and explains how you can build and test your\nchanges.\n\n**Haven't contributed to an open source project before?** No problem!\n[Contributing Guide](.github/CONTRIBUTING.md) has you covered as well.\n","funding_links":[],"categories":["JavaScript","Component"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresearchgate%2Freact-intersection-observer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresearchgate%2Freact-intersection-observer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresearchgate%2Freact-intersection-observer/lists"}