{"id":16191,"library":"react-intersection-observer","title":"React Intersection Observer","description":"react-intersection-observer is a comprehensive React library that provides hooks and components for efficiently leveraging the browser's Intersection Observer API. It allows developers to monitor when a React component enters or leaves the viewport, enabling features like lazy loading images, implementing infinite scrolling, or triggering animations based on visibility. The current stable version is `10.0.3`, and the project maintains an active release cadence, frequently pushing updates and bug fixes. Key differentiators include its dual API (hooks like `useInView` and `useOnInView`, plus a `<InView>` component), optimized performance through observer instance reuse, native API alignment, robust TypeScript support, and a tiny bundle size (around ~1.15kB for `useInView`). The `useOnInView` hook, introduced in v10, offers a no-re-render alternative for side-effect-heavy workloads like analytics tracking, further enhancing its utility. It also includes comprehensive test utilities for Jest and Vitest.","status":"active","version":"10.0.3","language":"javascript","source_language":"en","source_url":"https://github.com/thebuilder/react-intersection-observer","tags":["javascript","react","component","hooks","viewport","intersection","observer","lazy load","inview","typescript"],"install":[{"cmd":"npm install react-intersection-observer","lang":"bash","label":"npm"},{"cmd":"yarn add react-intersection-observer","lang":"bash","label":"yarn"},{"cmd":"pnpm add react-intersection-observer","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency for React applications.","package":"react","optional":false},{"reason":"Peer dependency for rendering React components to the DOM.","package":"react-dom","optional":false}],"imports":[{"note":"The primary hook for monitoring viewport intersection. Typically used with object or array destructuring for its return value. Prefer ESM imports.","wrong":"const useInView = require('react-intersection-observer').useInView;","symbol":"useInView","correct":"import { useInView } from 'react-intersection-observer';"},{"note":"A component-based API alternative to `useInView`, useful for simpler cases or class components. It is a named export, not a default export.","wrong":"import InView from 'react-intersection-observer';","symbol":"InView","correct":"import { InView } from 'react-intersection-observer';"},{"note":"Introduced in v10.0.0, this hook provides a callback-based API that avoids re-renders for side-effect-only scenarios. It's also a named export.","symbol":"useOnInView","correct":"import { useOnInView } from 'react-intersection-observer';"},{"note":"When using TypeScript, import `IntersectionObserverEntry` type for precise typing of the entry object received by the hooks/components.","symbol":"IntersectionObserverEntry","correct":"import type { IntersectionObserverEntry } from 'react-intersection-observer';"}],"quickstart":{"code":"import React from 'react';\nimport { useInView } from 'react-intersection-observer';\n\nconst LazyLoadedImage = ({ src, alt }) => {\n  const { ref, inView } = useInView({\n    triggerOnce: true, // Only trigger once when it enters the viewport\n    threshold: 0.1,    // Trigger when 10% of the element is visible\n  });\n\n  return (\n    <div ref={ref} style={{ height: '300px', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f0f0f0' }}>\n      {inView ? (\n        <img src={src} alt={alt} style={{ maxWidth: '100%', maxHeight: '100%' }} />\n      ) : (\n        <p style={{ color: '#888' }}>Loading image...</p>\n      )}\n    </div>\n  );\n};\n\nconst App = () => {\n  return (\n    <div>\n      <div style={{ height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>\n        <h1>Scroll Down to See Image</h1>\n      </div>\n      <LazyLoadedImage src=\"https://via.placeholder.com/600x300.png?text=Intersection+Observer\" alt=\"Placeholder Image\" />\n      <div style={{ height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>\n        <h2>End of Page</h2>\n      </div>\n    </div>\n  );\n};\n\nexport default App;\n","lang":"typescript","description":"This example demonstrates how to use the `useInView` hook to lazy load an image. The image only renders once it enters the viewport, using `triggerOnce` and a `threshold` of 0.1."},"warnings":[{"fix":"Upgrade your project's React and React-DOM dependencies to version 17.0.0 or higher. Alternatively, pin `react-intersection-observer` to a version below 9.14.0, such as `\"react-intersection-observer\": \"^9.13.0\"`.","message":"Support for React 15 and 16 has been officially dropped in version 9.14.0. Projects using older React versions will need to remain on a prior major version of `react-intersection-observer` or upgrade their React dependency.","severity":"breaking","affected_versions":">=9.14.0"},{"fix":"Wrap your `options` object in `React.useMemo` or define it outside the component, especially if it contains complex logic or changes on every render:\n```typescript\nconst options = React.useMemo(() => ({ threshold: 0.5, triggerOnce: true }), []);\nconst { ref, inView } = useInView(options);\n```","message":"The `options` object passed to `useInView` should be stable (e.g., memoized or defined outside the component) to prevent unnecessary re-creation of IntersectionObserver instances, which can lead to performance issues or unexpected behavior.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure you are on `react-intersection-observer@10.0.3` or newer to benefit from the fix regarding `triggerOnce` and merged refs. If upgrading is not immediately possible, consider alternative logic for `triggerOnce` or simplify ref merging.","message":"The `triggerOnce` option, when combined with merged refs, might skip the initial callback in some edge cases. This was addressed in v10.0.3.","severity":"gotcha","affected_versions":">=9.0.0 <10.0.3"},{"fix":"Be aware of this initial behavior. If you need to explicitly handle the initial 'out of view' state, you might need to use a `useEffect` hook with a default `inView` state or an explicit check outside the `useInView` callback.","message":"By design, the first `false` notification from the underlying Intersection Observer is ignored by `useInView` so that handlers only run after a 'real' visibility change. This means your `onChange` callback or `inView` state might not immediately reflect `false` on initial render if the element is not in view.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Ensure you are using ESM `import` statements: `import { useInView } from 'react-intersection-observer';`. If using TypeScript with CommonJS output, verify your `tsconfig.json` `module` option (e.g., `\"ESNext\"` or `\"Node16\"`) and your bundler's settings (e.g., Webpack, Rollup, Vite) support ESM correctly.","cause":"This typically occurs when trying to use named ESM exports with a CommonJS `require` call or when a bundler's configuration incorrectly transpiles modules.","error":"TypeError: (0 , react_intersection_observer__WEBPACK_IMPORTED_MODULE_2__.useInView) is not a function"},{"fix":"Move the `useInView` call directly into the body of your React functional component or into another custom hook. Hooks must always be called at the top level of a component or custom hook.","cause":"You are attempting to call `useInView` (or any other React Hook) inside a regular JavaScript function, an event handler, or a conditional block, violating the Rules of Hooks.","error":"React Hook 'useInView' cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function."},{"fix":"Double-check your `threshold` (e.g., `threshold: 0` means any part visible, `threshold: 1` means entirely visible) and `rootMargin` (e.g., `rootMargin: '10px 0px'` extends the root bounds). Ensure the `ref` returned by `useInView` is correctly assigned to the DOM element you intend to observe: `<div ref={ref}>...</div>`.","cause":"This often happens due to incorrect `threshold` values, `rootMargin` settings, or the `ref` not being correctly attached to the target DOM element.","error":"Element not entering 'inView' state despite being visible in the viewport."}],"ecosystem":"npm"}