diff --git a/__tests__/src/components/GalleryViewThumbnail.test.js b/__tests__/src/components/GalleryViewThumbnail.test.js
index 70ab6d78c3a58627245c642fc64b7bc16fce4589..3fbcc227c71cef8a14a5ed6800af599383a1b1f0 100644
--- a/__tests__/src/components/GalleryViewThumbnail.test.js
+++ b/__tests__/src/components/GalleryViewThumbnail.test.js
@@ -2,7 +2,7 @@ import React from 'react';
 import { shallow } from 'enzyme';
 import { Utils } from 'manifesto.js';
 import Chip from '@material-ui/core/Chip';
-import IntersectionObserver from '@researchgate/react-intersection-observer';
+import { InView } from 'react-intersection-observer';
 import manifestJson from '../../fixtures/version-2/019.json';
 import { GalleryViewThumbnail } from '../../../src/components/GalleryViewThumbnail';
 import IIIFThumbnail from '../../../src/containers/IIIFThumbnail';
@@ -78,7 +78,7 @@ describe('GalleryView', () => {
       };
       wrapper = createWrapper({ annotationsCount: 0, canvas, requestCanvasAnnotations });
 
-      wrapper.find(IntersectionObserver).simulate('change', { isIntersecting: true });
+      wrapper.find(InView).simulate('change', { isIntersecting: true });
       expect(requestCanvasAnnotations).toHaveBeenCalled();
     });
     it('does nothing if there is no intersection', () => {
@@ -89,7 +89,7 @@ describe('GalleryView', () => {
       };
       wrapper = createWrapper({ canvas, requestCanvasAnnotations });
 
-      wrapper.find(IntersectionObserver).simulate('change', { isIntersecting: false });
+      wrapper.find(InView).simulate('change', { isIntersecting: false });
       expect(requestCanvasAnnotations).not.toHaveBeenCalled();
     });
     it('does nothing if there are already some annotations', () => {
@@ -100,7 +100,7 @@ describe('GalleryView', () => {
       };
       wrapper = createWrapper({ annotationsCount: 5, canvas, requestCanvasAnnotations });
 
-      wrapper.find(IntersectionObserver).simulate('change', { isIntersecting: true });
+      wrapper.find(InView).simulate('change', { isIntersecting: true });
       expect(requestCanvasAnnotations).not.toHaveBeenCalled();
     });
   });
diff --git a/__tests__/src/components/IIIFThumbnail.test.js b/__tests__/src/components/IIIFThumbnail.test.js
index 5b60b112a9f825f34ae7f5d55713f0728d5bda00..8b2d3144229a068024aa15fc06106617bb71474f 100644
--- a/__tests__/src/components/IIIFThumbnail.test.js
+++ b/__tests__/src/components/IIIFThumbnail.test.js
@@ -1,6 +1,6 @@
 import React from 'react';
 import { shallow } from 'enzyme';
-import IntersectionObserver from '@researchgate/react-intersection-observer';
+import { InView } from 'react-intersection-observer';
 import Typography from '@material-ui/core/Typography';
 import { IIIFThumbnail } from '../../../src/components/IIIFThumbnail';
 
@@ -26,9 +26,9 @@ describe('IIIFThumbnail', () => {
   it('renders properly', () => {
     expect(wrapper.matchesElement(
       <div>
-        <IntersectionObserver onChange={wrapper.instance().handleIntersection}>
+        <InView onChange={wrapper.instance().handleIntersection}>
           <img alt="" />
-        </IntersectionObserver>
+        </InView>
       </div>,
     )).toBe(true);
   });
@@ -37,9 +37,9 @@ describe('IIIFThumbnail', () => {
     wrapper = createWrapper({});
     expect(wrapper.matchesElement(
       <div>
-        <IntersectionObserver onChange={wrapper.instance().handleIntersection}>
+        <InView onChange={wrapper.instance().handleIntersection}>
           <img alt="" />
-        </IntersectionObserver>
+        </InView>
       </div>,
     )).toBe(true);
     expect(wrapper.find('img').props().src).toMatch(/data:image\/png;base64/);
diff --git a/package.json b/package.json
index 5926f816211613ba7adbbb87f0b836861f940766..51f3192ff5e3e0b4dc908eed00b129aef2b4479c 100644
--- a/package.json
+++ b/package.json
@@ -37,14 +37,12 @@
     "@material-ui/core": "^4.12.3",
     "@material-ui/icons": "^4.9.1",
     "@material-ui/lab": "^4.0.0-alpha.53",
-    "@researchgate/react-intersection-observer": "^1.0.0",
     "classnames": "^2.2.6",
     "clsx": "^1.0.4",
     "deepmerge": "^4.2.2",
     "dompurify": "^2.0.11",
     "i18next": "^19.5.0",
     "icomcom-react": "^1.0.1",
-    "intersection-observer": "^0.10.0",
     "isomorphic-unfetch": "^3.0.0",
     "jss": "^10.3.0",
     "jss-rtl": "^0.3.0",
@@ -63,6 +61,7 @@
     "react-full-screen": "^1.1.1",
     "react-i18next": "^11.7.0",
     "react-image": "^4.0.1",
+    "react-intersection-observer": "^9.0.0",
     "react-mosaic-component": "^4.0.1",
     "react-redux": "^7.1.0 || ^8.0.0",
     "react-resize-observer": "^1.1.1",
diff --git a/src/components/GalleryViewThumbnail.js b/src/components/GalleryViewThumbnail.js
index acfe18aae196a5a4e489918c88a556042274800f..5ea6d433ec607696b21f293a4121d9358cc8ed7e 100644
--- a/src/components/GalleryViewThumbnail.js
+++ b/src/components/GalleryViewThumbnail.js
@@ -5,8 +5,7 @@ import Chip from '@material-ui/core/Chip';
 import AnnotationIcon from '@material-ui/icons/CommentSharp';
 import SearchIcon from '@material-ui/icons/SearchSharp';
 import classNames from 'classnames';
-import 'intersection-observer'; // polyfill needed for Safari
-import IntersectionObserver from '@researchgate/react-intersection-observer';
+import { InView } from 'react-intersection-observer';
 import MiradorCanvas from '../lib/MiradorCanvas';
 import IIIFThumbnail from '../containers/IIIFThumbnail';
 
@@ -109,7 +108,7 @@ export class GalleryViewThumbnail extends Component {
     const miradorCanvas = new MiradorCanvas(canvas);
 
     return (
-      <IntersectionObserver onChange={this.handleIntersection}>
+      <InView onChange={this.handleIntersection}>
         <div
           key={canvas.index}
           className={
@@ -168,7 +167,7 @@ export class GalleryViewThumbnail extends Component {
             </div>
           </IIIFThumbnail>
         </div>
-      </IntersectionObserver>
+      </InView>
     );
   }
 }
diff --git a/src/components/IIIFThumbnail.js b/src/components/IIIFThumbnail.js
index c942f4824231739d722ab08c9682abdc3165148f..a4f036f6d731c3fdf17114c505c9e252b270fd23 100644
--- a/src/components/IIIFThumbnail.js
+++ b/src/components/IIIFThumbnail.js
@@ -1,8 +1,7 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import 'intersection-observer'; // polyfill needed for Safari
 import Typography from '@material-ui/core/Typography';
-import IntersectionObserver from '@researchgate/react-intersection-observer';
+import { InView } from 'react-intersection-observer';
 import classNames from 'classnames';
 import getThumbnail from '../lib/ThumbnailFactory';
 
@@ -48,10 +47,10 @@ export class IIIFThumbnail extends Component {
    * Handles the intersection (visibility) of a given thumbnail, by requesting
    * the image and then updating the state.
    */
-  handleIntersection(event) {
+  handleIntersection(inView, _entry) {
     const { loaded } = this.state;
 
-    if (loaded || !event.isIntersecting) return;
+    if (loaded || !inView) return;
 
     this.setState(state => ({ ...state, loaded: true }));
   }
@@ -152,7 +151,7 @@ export class IIIFThumbnail extends Component {
 
     return (
       <div className={classNames(classes.root, { [classes[`${variant}Root`]]: variant })}>
-        <IntersectionObserver onChange={this.handleIntersection}>
+        <InView as="span" onChange={this.handleIntersection}>
           <img
             alt=""
             role="presentation"
@@ -160,7 +159,7 @@ export class IIIFThumbnail extends Component {
             style={this.imageStyles()}
             className={classes.image}
           />
-        </IntersectionObserver>
+        </InView>
         { labelled && (
           <div className={classNames(classes.label, { [classes[`${variant}Label`]]: variant })}>
             <Typography variant="caption" classes={{ root: classNames(classes.caption, { [classes[`${variant}Caption`]]: variant }) }}>