@@ -12,9 +12,6 @@ to tell you when an element enters or leaves the viewport. Contains both a
12
12
[ Hooks] ( #useinview-hook ) , [ render props] ( #render-props ) and
13
13
[ plain children] ( #plain-children ) implementation.
14
14
15
- ** Storybook Demo:**
16
- [ https://react-intersection-observer.vercel.app ] ( https://react-intersection-observer.vercel.app )
17
-
18
15
## Features
19
16
20
17
- 🪝 ** Hooks or Component API** - With ` useInView ` it's easier than ever to
@@ -30,15 +27,11 @@ to tell you when an element enters or leaves the viewport. Contains both a
30
27
- 💥 ** Tiny bundle** - Around ** ~ 1.15kB** for ` useInView ` and ** ~ 1.6kB** for
31
28
` <InView> `
32
29
33
- ## Installation
34
-
35
- Install using [ Yarn] ( https://yarnpkg.com ) :
30
+ [ ![ Open in StackBlitz] ( https://developer.stackblitz.com/img/open_in_stackblitz.svg )] ( https://stackblitz.com/github/thebuilder/react-intersection-observer )
36
31
37
- ``` sh
38
- yarn add react-intersection-observer
39
- ```
32
+ ## Installation
40
33
41
- or NPM :
34
+ Install the package with your package manager of choice :
42
35
43
36
``` sh
44
37
npm install react-intersection-observer --save
@@ -65,8 +58,8 @@ Assign the `ref` to the DOM element you want to monitor, and the hook will
65
58
report the status.
66
59
67
60
``` jsx
68
- import React from ' react' ;
69
- import { useInView } from ' react-intersection-observer' ;
61
+ import React from " react" ;
62
+ import { useInView } from " react-intersection-observer" ;
70
63
71
64
const Component = () => {
72
65
const { ref , inView , entry } = useInView ({
@@ -82,8 +75,6 @@ const Component = () => {
82
75
};
83
76
```
84
77
85
- [ ![ Edit useInView] ( https://codesandbox.io/static/img/play-codesandbox.svg )] ( https://codesandbox.io/s/useinview-ud2vo?fontsize=14&hidenavigation=1&theme=dark )
86
-
87
78
### Render props
88
79
89
80
To use the ` <InView> ` component, you pass it a function. It will be called
@@ -98,7 +89,7 @@ on `entry`, giving you access to all the details about the current intersection
98
89
state.
99
90
100
91
``` jsx
101
- import { InView } from ' react-intersection-observer' ;
92
+ import { InView } from " react-intersection-observer" ;
102
93
103
94
const Component = () => (
104
95
< InView>
@@ -113,8 +104,6 @@ const Component = () => (
113
104
export default Component ;
114
105
```
115
106
116
- [ ![ Edit InView render props] ( https://codesandbox.io/static/img/play-codesandbox.svg )] ( https://codesandbox.io/s/inview-render-props-hvhcb?fontsize=14&hidenavigation=1&theme=dark )
117
-
118
107
### Plain children
119
108
120
109
You can pass any element to the ` <InView /> ` , and it will handle creating the
@@ -123,19 +112,17 @@ state in your own component. Any extra props you add to `<InView>` will be
123
112
passed to the HTML element, allowing you set the ` className ` , ` style ` , etc.
124
113
125
114
``` jsx
126
- import { InView } from ' react-intersection-observer' ;
115
+ import { InView } from " react-intersection-observer" ;
127
116
128
117
const Component = () => (
129
- < InView as= " div" onChange= {(inView , entry ) => console .log (' Inview:' , inView)}>
118
+ < InView as= " div" onChange= {(inView , entry ) => console .log (" Inview:" , inView)}>
130
119
< h2> Plain children are always rendered . Use onChange to monitor state.< / h2>
131
120
< / InView>
132
121
);
133
122
134
123
export default Component ;
135
124
```
136
125
137
- [ ![ Edit InView plain children] ( https://codesandbox.io/static/img/play-codesandbox.svg )] ( https://codesandbox.io/s/inview-plain-children-vv51y?fontsize=14&hidenavigation=1&theme=dark )
138
-
139
126
> ** Note** <br > When rendering a plain child, make sure you keep your HTML output
140
127
> semantic. Change the ` as ` to match the context, and add a ` className ` to style
141
128
> the ` <InView /> ` . The component does not support Ref Forwarding, so if you
@@ -149,7 +136,7 @@ Provide these as the options argument in the `useInView` hook or as props on the
149
136
** ` <InView /> ` ** component.
150
137
151
138
| Name | Type | Default | Description |
152
- | ------------------------ | --------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
139
+ | ---------------------- | ------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
153
140
| ** root** | ` Element ` | ` document ` | The Intersection Observer interface's read-only root property identifies the Element or Document whose bounds are treated as the bounding box of the viewport for the element which is the observer's target. If the root is ` null ` , then the bounds of the actual document viewport are used. |
154
141
| ** rootMargin** | ` string ` | ` '0px' ` | Margin around the root. Can have values similar to the CSS margin property, e.g. ` "10px 20px 30px 40px" ` (top, right, bottom, left). Also supports percentages, to check if an element intersects with the center of the viewport for example "-50% 0% -50% 0%". |
155
142
| ** threshold** | ` number ` or ` number[] ` | ` 0 ` | Number between ` 0 ` and ` 1 ` indicating the percentage that should be visible before triggering. Can also be an array of numbers, to create multiple trigger points. |
@@ -166,7 +153,7 @@ Provide these as the options argument in the `useInView` hook or as props on the
166
153
The ** ` <InView /> ` ** component also accepts the following props:
167
154
168
155
| Name | Type | Default | Description |
169
- | -------------- | ------------------------------------------------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
156
+ | ------------ | ---------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
170
157
| ** as** | ` IntrinsicElement ` | ` 'div' ` | Render the wrapping element as this element. Defaults to ` div ` . If you want to use a custom component, please use the ` useInView ` hook or a render prop instead to manage the reference explictly. |
171
158
| ** children** | ` ({ref, inView, entry}) => ReactNode ` or ` ReactNode ` | ` undefined ` | Children expects a function that receives an object containing the ` inView ` boolean and a ` ref ` that should be assigned to the element root. Alternatively pass a plain child, to have the ` <InView /> ` deal with the wrapping element. You will also get the ` IntersectionObserverEntry ` as ` entry ` , giving you more details. |
172
159
@@ -213,8 +200,8 @@ few ideas for how you can use it.
213
200
You can wrap multiple ` ref` assignments in a single ` useCallback` :
214
201
215
202
` ` ` jsx
216
- import React , { useRef , useCallback } from ' react' ;
217
- import { useInView } from ' react-intersection-observer' ;
203
+ import React , { useRef , useCallback } from " react" ;
204
+ import { useInView } from " react-intersection-observer" ;
218
205
219
206
function Component (props ) {
220
207
const ref = useRef ();
@@ -259,7 +246,7 @@ will emulate the real IntersectionObserver, allowing you to validate that your
259
246
components are behaving as expected.
260
247
261
248
| Method | Description |
262
- |-----------------------------------------------| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
249
+ | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
250
| ` mockAllIsIntersecting (isIntersecting)` | Set ` isIntersecting` on all current Intersection Observer instances. The value of ` isIntersecting` should be either a ` boolean` or a threshold between 0 and 1. |
264
251
| ` mockIsIntersecting (element, isIntersecting)` | Set ` isIntersecting` for the Intersection Observer of a specific ` element` . The value of ` isIntersecting` should be either a ` boolean` or a threshold between 0 and 1. |
265
252
| ` intersectionMockInstance (element)` | Call the ` intersectionMockInstance` method with an element, to get the (mocked) ` IntersectionObserver` instance. You can use this to spy on the ` observe` and` unobserve` methods. |
@@ -285,11 +272,11 @@ Jest. Otherwise, you'll need to manually setup/reset the mocking in either the
285
272
individual tests, or a [setup file](https://vitest.dev/config/#setupfiles).
286
273
287
274
` ` ` js
288
- import { vi , beforeEach , afterEach } from ' vitest' ;
275
+ import { vi , beforeEach , afterEach } from " vitest" ;
289
276
import {
290
277
setupIntersectionMocking ,
291
278
resetIntersectionMocking ,
292
- } from ' react-intersection-observer/test-utils' ;
279
+ } from " react-intersection-observer/test-utils" ;
293
280
294
281
beforeEach (() => {
295
282
setupIntersectionMocking (vi .fn );
@@ -321,7 +308,7 @@ were you actively import `react-intersection-observer/test-utils`.
321
308
**test-setup.js**
322
309
323
310
` ` ` js
324
- import { defaultFallbackInView } from ' react-intersection-observer' ;
311
+ import { defaultFallbackInView } from " react-intersection-observer" ;
325
312
326
313
defaultFallbackInView (true ); // or `false` - whichever consistent behavior makes the most sense for your use case.
327
314
` ` `
@@ -334,21 +321,21 @@ Vitest.
334
321
335
322
` ` ` js
336
323
module .exports = {
337
- setupFilesAfterEnv: [' react-intersection-observer/test-utils' ],
324
+ setupFilesAfterEnv: [" react-intersection-observer/test-utils" ],
338
325
};
339
326
` ` `
340
327
341
328
### Test Example
342
329
343
330
` ` ` js
344
- import React from ' react' ;
345
- import { screen , render } from ' @testing-library/react' ;
346
- import { useInView } from ' react-intersection-observer' ;
331
+ import React from " react" ;
332
+ import { screen , render } from " @testing-library/react" ;
333
+ import { useInView } from " react-intersection-observer" ;
347
334
import {
348
335
mockAllIsIntersecting ,
349
336
mockIsIntersecting ,
350
337
intersectionMockInstance ,
351
- } from ' react-intersection-observer/test-utils' ;
338
+ } from " react-intersection-observer/test-utils" ;
352
339
353
340
const HookComponent = ({ options }) => {
354
341
const { ref , inView } = useInView (options);
@@ -359,37 +346,37 @@ const HookComponent = ({ options }) => {
359
346
);
360
347
};
361
348
362
- test (' should create a hook inView' , () => {
363
- render (< HookComponent/ > );
349
+ test (" should create a hook inView" , () => {
350
+ render (< HookComponent / > );
364
351
365
352
// This causes all (existing) IntersectionObservers to be set as intersecting
366
353
mockAllIsIntersecting (true );
367
- screen .getByText (' true' );
354
+ screen .getByText (" true" );
368
355
});
369
356
370
- test (' should create a hook inView with threshold' , () => {
371
- render (< HookComponent options= {{ threshold: 0.3 }}/ > );
357
+ test (" should create a hook inView with threshold" , () => {
358
+ render (< HookComponent options= {{ threshold: 0.3 }} / > );
372
359
373
360
mockAllIsIntersecting (0.1 );
374
- screen .getByText (' false' );
361
+ screen .getByText (" false" );
375
362
376
363
// Once the threshold has been passed, it will trigger inView.
377
364
mockAllIsIntersecting (0.3 );
378
- screen .getByText (' true' );
365
+ screen .getByText (" true" );
379
366
});
380
367
381
- test (' should mock intersecing on specific hook' , () => {
382
- render (< HookComponent/ > );
383
- const wrapper = screen .getByTestId (' wrapper' );
368
+ test (" should mock intersecing on specific hook" , () => {
369
+ render (< HookComponent / > );
370
+ const wrapper = screen .getByTestId (" wrapper" );
384
371
385
372
// Set the intersection state on the wrapper.
386
373
mockIsIntersecting (wrapper, 0.5 );
387
- screen .getByText (' true' );
374
+ screen .getByText (" true" );
388
375
});
389
376
390
- test (' should create a hook and call observe' , () => {
391
- const { getByTestId } = render (< HookComponent/ > );
392
- const wrapper = getByTestId (' wrapper' );
377
+ test (" should create a hook and call observe" , () => {
378
+ const { getByTestId } = render (< HookComponent / > );
379
+ const wrapper = getByTestId (" wrapper" );
393
380
// Access the `IntersectionObserver` instance for the wrapper Element.
394
381
const instance = intersectionMockInstance (wrapper);
395
382
@@ -422,7 +409,7 @@ application can correctly handle all your observers firing either `true` or
422
409
You can set the fallback globally:
423
410
424
411
` ` ` js
425
- import { defaultFallbackInView } from ' react-intersection-observer' ;
412
+ import { defaultFallbackInView } from " react-intersection-observer" ;
426
413
427
414
defaultFallbackInView (true ); // or 'false'
428
415
` ` `
@@ -431,8 +418,8 @@ You can also define the fallback locally on `useInView` or `<InView>` as an
431
418
option. This will override the global fallback value.
432
419
433
420
` ` ` jsx
434
- import React from ' react' ;
435
- import { useInView } from ' react-intersection-observer' ;
421
+ import React from " react" ;
422
+ import { useInView } from " react-intersection-observer" ;
436
423
437
424
const Component = () => {
438
425
const { ref , inView , entry } = useInView ({
@@ -461,7 +448,7 @@ yarn add intersection-observer
461
448
Then import it in your app:
462
449
463
450
` ` ` js
464
- import ' intersection-observer' ;
451
+ import " intersection-observer" ;
465
452
` ` `
466
453
467
454
If you are using Webpack (or similar) you could use
@@ -474,8 +461,8 @@ like this:
474
461
* Do feature detection, to figure out which polyfills needs to be imported.
475
462
**/
476
463
async function loadPolyfills () {
477
- if (typeof window .IntersectionObserver === ' undefined' ) {
478
- await import (' intersection-observer' );
464
+ if (typeof window .IntersectionObserver === " undefined" ) {
465
+ await import (" intersection-observer" );
479
466
}
480
467
}
481
468
` ` `
@@ -488,13 +475,13 @@ IntersectionObserver instances. This allows you to handle more advanced use
488
475
cases, where you need full control over when and how observers are created.
489
476
490
477
` ` ` js
491
- import { observe } from ' react-intersection-observer' ;
478
+ import { observe } from " react-intersection-observer" ;
492
479
493
480
const destroy = observe (element, callback, options);
494
481
` ` `
495
482
496
483
| Name | Type | Required | Description |
497
- |--------------| ----------------------------| ----------| ------------------------------------------------------------ |
484
+ | ------------ | -------------------------- | -------- | ---------------------------------------------------------- |
498
485
| **element** | ` Element ` | true | DOM element to observe |
499
486
| **callback** | ` ObserverInstanceCallback` | true | The callback function that Intersection Observer will call |
500
487
| **options** | ` IntersectionObserverInit` | false | The options for the Intersection Observer |
@@ -507,29 +494,13 @@ order to destroy the observer again.
507
494
> how instances are created.
508
495
509
496
[package-url]: https://npmjs.org/package/react-intersection-observer
510
-
511
497
[npm-version-svg]: https://img.shields.io/npm/v/react-intersection-observer.svg
512
-
513
- [npm-minzip-svg]:
514
- https://img.shields.io/bundlephobia/minzip/react-intersection-observer.svg
515
-
516
- [bundlephobia-url]:
517
- https://bundlephobia.com/result?p=react-intersection-observer
518
-
498
+ [npm-minzip-svg]: https://img.shields.io/bundlephobia/minzip/react-intersection-observer.svg
499
+ [bundlephobia-url]: https://bundlephobia.com/result?p=react-intersection-observer
519
500
[license-image]: http://img.shields.io/npm/l/react-intersection-observer.svg
520
-
521
501
[license-url]: LICENSE
522
-
523
502
[downloads-image]: http://img.shields.io/npm/dm/react-intersection-observer.svg
524
-
525
- [downloads-url]:
526
- http://npm-stat.com/charts.html?package=react-intersection-observer
527
-
528
- [test-image]:
529
- https://github.com/thebuilder/react-intersection-observer/workflows/Test/badge.svg
530
-
531
- [test-url]:
532
- https://github.com/thebuilder/react-intersection-observer/actions?query=workflow%3ATest
533
-
534
- [test-utils-url]:
535
- https://github.com/thebuilder/react-intersection-observer/blob/master/src/test-utils.ts
503
+ [downloads-url]: http://npm-stat.com/charts.html?package=react-intersection-observer
504
+ [test-image]: https://github.com/thebuilder/react-intersection-observer/workflows/Test/badge.svg
505
+ [test-url]: https://github.com/thebuilder/react-intersection-observer/actions?query=workflow%3ATest
506
+ [test-utils-url]: https://github.com/thebuilder/react-intersection-observer/blob/master/src/test-utils.ts
0 commit comments