React component that triggers a function when the component enters or leaves the viewport. No complex configuration needed, just wrap your views and it handles the events.
import Observer from 'react-intersection-observer'
const Component = () => (
<Observer>
{inView => <h2>{`Header inside viewport ${inView}.`}</h2>}
</Observer>
)
export default Component
See https://thebuilder.github.io/react-intersection-observer/ for a demo.
This module is used in
react-scroll-percentage
to monitor the scroll position of elements in view. This module is also a great
example of using react-intersection-observer
as the basis for more complex
needs.
Install using Yarn:
yarn add react-intersection-observer
or NPM:
npm install react-intersection-observer --save
The component requires the intersection-observer API to be available on the global namespace. At the moment you should include a polyfill to ensure support in all browsers.
You can import the polyfill directly or use a service like polyfill.io that can add it when needed.
yarn add intersection-observer
Then import it in your app:
import 'intersection-observer'
If you are using Webpack (or similar) you could use dynamic imports, to load the Polyfill only if needed. A basic implementation could look something like this:
loadPolyfills()
.then(() => /* Render React application now that your Polyfills are ready */)
/**
* Do feature detection, to figure out which polyfills needs to be imported.
**/
function loadPolyfills() {
const polyfills = []
if (!supportsIntersectionObserver()) {
polyfills.push(import('intersection-observer'))
}
return Promise.all(polyfills)
}
function supportsIntersectionObserver() {
return (
'IntersectionObserver' in global &&
'IntersectionObserverEntry' in global &&
'intersectionRatio' in IntersectionObserverEntry.prototype
)
}
The <Observer />
accepts the following props:
Name | Type | Default | Required | Description |
---|---|---|---|---|
children | func/node | true | Children should be either a function or a node | |
root | HTMLElement | false | The HTMLElement that is used as the viewport for checking visibility of the target. Defaults to the browser viewport if not specified or if null. | |
rootId | String | false | Unique identifier for the root element - This is used to identify the IntersectionObserver instance, so it can be reused. If you defined a root element, without adding an id, it will create a new instance for all components. | |
rootMargin | String | '0px' | false | Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). |
tag | String | 'div' | false | Element tag to use for the wrapping component |
threshold | Number | 0 | false | Number between 0 and 1 indicating the the percentage that should be visible before triggering. Can also be an array of numbers, to create multiple trigger points. |
triggerOnce | Bool | false | false | Only trigger this method once |
onChange | Func | false | Call this function whenever the in view state changes | |
render | Func | false | Use render method to only render content when inView | |
innerRef | Func | false | Get a reference to the the inner DOM node |
The default way to use the Observer
, is to pass a function as the child. It
will be called whenever the state changes, with the new value of inView
.
import Observer from 'react-intersection-observer'
const Component = () => (
<Observer>
{inView => <h2>{`Header inside viewport ${inView}.`}</h2>}
</Observer>
)
export default Component
For simple use cases where you want to only render a component when it enters
view, you can use the render
prop.
import Observer from 'react-intersection-observer'
const Component = () => (
<Observer
style={{ height: 200, position: 'relative' }}
render={() => (
<div
style={{
position: 'absolute',
top: 0,
bottom: 0,
}}
>
<p>
{
'Make sure that the Observer controls the height, so it does not change change when element is added.'
}
</p>
</div>
)}
/>
)
export default Component
You can monitor the onChange method, and control the state in your own component. The child node will always be rendered.
import Observer from 'react-intersection-observer'
const Component = () => (
<Observer onChange={inView => console.log('Inview:', inView)}>
<h2>Plain children are always rendered. Use onChange to monitor state.</h2>
</Observer>
)
export default Component