A React component to execute functions when scrolling to specific elements in any scrollable container
npx @tessl/cli install tessl/npm-react-waypoint@10.3.00
# React Waypoint
1
2
React Waypoint is a React component that executes callback functions whenever you scroll to an element. It works in all containers that can scroll, including the browser window, and provides callbacks for enter/leave events when elements cross viewport boundaries.
3
4
## Package Information
5
6
- **Package Name**: react-waypoint
7
- **Package Type**: npm
8
- **Language**: JavaScript (with TypeScript definitions included)
9
- **Installation**: `npm install react-waypoint`
10
11
## Core Imports
12
13
```javascript
14
import { Waypoint } from 'react-waypoint';
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { Waypoint } = require('react-waypoint');
21
```
22
23
## Basic Usage
24
25
```jsx
26
import React from 'react';
27
import { Waypoint } from 'react-waypoint';
28
29
function App() {
30
const handleWaypointEnter = (args) => {
31
console.log('Waypoint entered!', args.currentPosition);
32
};
33
34
const handleWaypointLeave = (args) => {
35
console.log('Waypoint left!', args.currentPosition);
36
};
37
38
return (
39
<div>
40
<div style={{ height: '1000px' }}>Scroll down...</div>
41
42
{/* Basic waypoint */}
43
<Waypoint
44
onEnter={handleWaypointEnter}
45
onLeave={handleWaypointLeave}
46
/>
47
48
{/* Waypoint with child element */}
49
<Waypoint onEnter={handleWaypointEnter}>
50
<div style={{ padding: '20px', background: '#f0f0f0' }}>
51
This content triggers waypoint events
52
</div>
53
</Waypoint>
54
</div>
55
);
56
}
57
```
58
59
## Architecture
60
61
React Waypoint operates through several key mechanisms:
62
63
- **Event-Driven Architecture**: Uses scroll and resize event listeners to detect position changes
64
- **Position Calculation**: Calculates element position relative to viewport using `getBoundingClientRect()`
65
- **Offset Support**: Supports both pixel and percentage-based offsets for flexible triggering
66
- **Scrollable Ancestor Detection**: Automatically finds the closest scrollable container or allows custom specification
67
- **Lazy Evaluation**: Uses `onNextTick` scheduling to optimize performance and avoid layout thrashing
68
69
## Capabilities
70
71
### Waypoint Component
72
73
The main React component that provides scroll-based element visibility detection.
74
75
```typescript { .api }
76
class Waypoint extends React.Component<WaypointProps, {}> {
77
static above: string;
78
static below: string;
79
static inside: string;
80
static invisible: string;
81
}
82
83
interface WaypointProps {
84
/** Function called when waypoint enters viewport */
85
onEnter?: (args: CallbackArgs) => void;
86
/** Function called when waypoint leaves viewport */
87
onLeave?: (args: CallbackArgs) => void;
88
/** Function called when waypoint position changes */
89
onPositionChange?: (args: CallbackArgs) => void;
90
/** Whether to activate on horizontal scrolling instead of vertical */
91
horizontal?: boolean;
92
/** Distance from top of container in pixels or percentage (e.g., "20px", "20%") */
93
topOffset?: string | number;
94
/** Distance from bottom of container in pixels or percentage (e.g., "20px", "20%") */
95
bottomOffset?: string | number;
96
/** Custom ancestor to determine if the target is visible in it */
97
scrollableAncestor?: any;
98
/** If the onEnter/onLeave events are to be fired on rapid scrolling */
99
fireOnRapidScroll?: boolean;
100
/** Use this prop to get debug information in the console log */
101
debug?: boolean;
102
/** Child elements to wrap with waypoint functionality */
103
children?: React.ReactNode;
104
}
105
106
interface CallbackArgs {
107
/** The position that the waypoint has at the moment */
108
currentPosition: string;
109
/** The position that the waypoint had before */
110
previousPosition: string;
111
/** The native scroll event that triggered the callback (may be missing) */
112
event?: Event;
113
/** The waypoint's distance to the top of the viewport */
114
waypointTop: number;
115
/** The waypoint's distance to the bottom of the viewport */
116
waypointBottom: number;
117
/** The distance from the scrollable ancestor to the viewport top */
118
viewportTop: number;
119
/** The distance from the bottom of the scrollable ancestor to the viewport top */
120
viewportBottom: number;
121
}
122
```
123
124
### Position Constants
125
126
Static constants representing different waypoint positions relative to the viewport.
127
128
```typescript { .api }
129
/** Waypoint is above the viewport */
130
Waypoint.above: string; // "above"
131
/** Waypoint is below the viewport */
132
Waypoint.below: string; // "below"
133
/** Waypoint is inside (visible in) the viewport */
134
Waypoint.inside: string; // "inside"
135
/** Waypoint is invisible (e.g., display: none) */
136
Waypoint.invisible: string; // "invisible"
137
```
138
139
### Callback Events
140
141
React Waypoint provides three main callback events for different use cases:
142
143
**onEnter**: Triggered when the waypoint becomes visible in the viewport
144
```jsx
145
<Waypoint
146
onEnter={({ currentPosition, previousPosition, event, waypointTop, waypointBottom, viewportTop, viewportBottom }) => {
147
console.log('Element entered viewport');
148
// Useful for: lazy loading content, analytics tracking, animations
149
}}
150
/>
151
```
152
153
**onLeave**: Triggered when the waypoint leaves the viewport
154
```jsx
155
<Waypoint
156
onLeave={({ currentPosition, previousPosition, event, waypointTop, waypointBottom, viewportTop, viewportBottom }) => {
157
console.log('Element left viewport');
158
// Useful for: cleanup, pausing videos, stopping animations
159
}}
160
/>
161
```
162
163
**onPositionChange**: Triggered whenever the waypoint's position changes
164
```jsx
165
<Waypoint
166
onPositionChange={({ currentPosition, previousPosition, event, waypointTop, waypointBottom, viewportTop, viewportBottom }) => {
167
console.log(\`Position changed from \${previousPosition} to \${currentPosition}\`);
168
// Useful for: scroll spies, position-based UI updates
169
}}
170
/>
171
```
172
173
### Offset Configuration
174
175
Control when waypoint events trigger using flexible offset options:
176
177
**Percentage-based offsets** (relative to container height):
178
```jsx
179
<Waypoint
180
topOffset="20%" // Trigger 20% from top of viewport
181
bottomOffset="10%" // Trigger 10% from bottom of viewport
182
/>
183
```
184
185
**Pixel-based offsets**:
186
```jsx
187
<Waypoint
188
topOffset={100} // Trigger 100px from top
189
bottomOffset="50px" // Trigger 50px from bottom
190
/>
191
```
192
193
**Negative offsets** (trigger before entering viewport):
194
```jsx
195
<Waypoint
196
topOffset="-100px" // Trigger 100px before entering viewport
197
bottomOffset="-20%" // Trigger when 20% outside bottom
198
/>
199
```
200
201
### Horizontal Scrolling
202
203
Enable horizontal scroll detection for side-scrolling interfaces:
204
205
```jsx
206
<Waypoint
207
horizontal={true}
208
onEnter={() => console.log('Entered horizontal viewport')}
209
/>
210
```
211
212
### Custom Scrollable Containers
213
214
Specify a custom scrollable ancestor instead of auto-detection:
215
216
```jsx
217
function ScrollableContainer() {
218
const containerRef = useRef(null);
219
220
return (
221
<div ref={containerRef} style={{ height: '300px', overflow: 'auto' }}>
222
<div style={{ height: '1000px' }}>
223
<Waypoint
224
scrollableAncestor={containerRef.current}
225
onEnter={() => console.log('Entered custom container viewport')}
226
/>
227
</div>
228
</div>
229
);
230
}
231
```
232
233
### Rapid Scroll Handling
234
235
Control behavior during rapid scrolling where waypoint might be skipped:
236
237
```jsx
238
<Waypoint
239
fireOnRapidScroll={true} // Default: fires both onEnter and onLeave during rapid scroll
240
fireOnRapidScroll={false} // Skip events during rapid scroll
241
/>
242
```
243
244
### Debug Mode
245
246
Enable detailed console logging for development:
247
248
```jsx
249
<Waypoint
250
debug={true}
251
onEnter={() => console.log('Debug information will show in console')}
252
/>
253
```
254
255
### Child Element Wrapping
256
257
Wrap existing elements to track their visibility:
258
259
```jsx
260
{/* Without children - creates invisible span element */}
261
<Waypoint onEnter={handleEnter} />
262
263
{/* With children - tracks the child element */}
264
<Waypoint onEnter={handleEnter}>
265
<img src="image.jpg" alt="Lazy loaded image" />
266
</Waypoint>
267
268
{/* Multiple children */}
269
<Waypoint onEnter={handleEnter}>
270
<div className="content-section">
271
<h2>Section Title</h2>
272
<p>Section content...</p>
273
</div>
274
</Waypoint>
275
```
276
277
## Common Use Cases
278
279
**Lazy Loading Images**:
280
```jsx
281
<Waypoint
282
onEnter={() => setImageSrc(actualImageUrl)}
283
topOffset="-100px" // Start loading before entering viewport
284
>
285
<img src={imageSrc || placeholderSrc} alt="Lazy loaded" />
286
</Waypoint>
287
```
288
289
**Infinite Scroll**:
290
```jsx
291
<Waypoint
292
key={currentPage} // Important: use key to recreate waypoint
293
onEnter={loadMoreContent}
294
bottomOffset="100px" // Trigger before reaching bottom
295
/>
296
```
297
298
**Scroll Spy Navigation**:
299
```jsx
300
<Waypoint
301
onPositionChange={({ currentPosition }) => {
302
if (currentPosition === 'inside') {
303
setActiveSection('section1');
304
}
305
}}
306
>
307
<section id="section1">Content...</section>
308
</Waypoint>
309
```
310
311
**Analytics Tracking**:
312
```jsx
313
<Waypoint
314
onEnter={() => analytics.track('Section Viewed')}
315
topOffset="50%" // Trigger when 50% visible
316
/>
317
```