Convert Figma logo to code with AI

civiccc logoreact-waypoint

A React component to execute a function whenever you scroll to an element.

4,080
208
4,080
61

Top Related Projects

Sensor component for React that notifies you when it goes in or out of the window viewport.

React component for the Intersection <Observer /> API

🔎 🏞 The original medium.com-inspired image zooming library for React (since 2016)

It is a trap! A lock for a Focus. 🔓

📏 Compute measurements of a React component.

Quick Overview

React Waypoint is a React component that allows you to execute a function whenever you scroll to an element. It's particularly useful for implementing infinite scrolling, lazy loading images, or triggering animations when an element enters the viewport. This library provides a simple and efficient way to detect when a component enters or leaves the visible area of the screen.

Pros

  • Easy to integrate with existing React applications
  • Highly customizable with various props for fine-tuning behavior
  • Supports both vertical and horizontal scrolling
  • Lightweight and performant

Cons

  • Limited to React applications only
  • May require additional setup for complex scrolling scenarios
  • Documentation could be more comprehensive
  • Potential for performance issues if overused in a single page

Code Examples

  1. Basic usage:
import Waypoint from 'react-waypoint';

function MyComponent() {
  return (
    <div>
      <Waypoint
        onEnter={({ previousPosition, currentPosition, event }) => {
          // Do something when the waypoint enters the viewport
        }}
        onLeave={({ previousPosition, currentPosition, event }) => {
          // Do something when the waypoint leaves the viewport
        }}
      />
      <div>My content</div>
    </div>
  );
}
  1. Implementing infinite scroll:
import Waypoint from 'react-waypoint';

function InfiniteScrollList({ items, loadMore }) {
  return (
    <div>
      {items.map((item) => (
        <div key={item.id}>{item.content}</div>
      ))}
      <Waypoint onEnter={loadMore} bottomOffset="-200px" />
    </div>
  );
}
  1. Triggering animations:
import Waypoint from 'react-waypoint';
import { useState } from 'react';

function AnimatedComponent() {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <Waypoint onEnter={() => setIsVisible(true)} />
      <div className={isVisible ? 'fade-in' : ''}>
        This content will fade in when it enters the viewport
      </div>
    </div>
  );
}

Getting Started

  1. Install the package:

    npm install react-waypoint
    
  2. Import and use in your React component:

    import Waypoint from 'react-waypoint';
    
    function MyComponent() {
      return (
        <div>
          <Waypoint
            onEnter={() => console.log('Waypoint entered')}
            onLeave={() => console.log('Waypoint left')}
          />
          <div>Content to track</div>
        </div>
      );
    }
    
  3. Customize the Waypoint behavior using props like topOffset, bottomOffset, or horizontal as needed.

Competitor Comparisons

Sensor component for React that notifies you when it goes in or out of the window viewport.

Pros of react-visibility-sensor

  • More lightweight and focused solely on visibility detection
  • Offers more granular control over visibility thresholds
  • Provides a simpler API with fewer configuration options

Cons of react-visibility-sensor

  • Less actively maintained compared to react-waypoint
  • Fewer additional features beyond basic visibility detection
  • May have compatibility issues with newer React versions

Code Comparison

react-visibility-sensor:

<VisibilitySensor onChange={this.onChange}>
  <div>{"I'm the element to detect visibility for"}</div>
</VisibilitySensor>

react-waypoint:

<Waypoint
  onEnter={this.handleWaypointEnter}
  onLeave={this.handleWaypointLeave}
>
  <div>{"I'm the waypoint"}</div>
</Waypoint>

Both libraries provide similar functionality for detecting when an element enters or leaves the viewport. react-visibility-sensor uses a more straightforward approach with a single onChange callback, while react-waypoint offers separate onEnter and onLeave callbacks for more specific control.

react-waypoint generally provides more features and flexibility, including horizontal scrolling support and customizable offsets. However, react-visibility-sensor may be preferred for simpler use cases or when a more lightweight solution is needed.

React component for the Intersection <Observer /> API

Pros of react-intersection-observer

  • Uses the modern Intersection Observer API, which is more performant than scroll events
  • Supports multiple instances without performance degradation
  • Provides more configuration options and flexibility

Cons of react-intersection-observer

  • Requires polyfill for older browsers that don't support Intersection Observer API
  • Slightly more complex API compared to react-waypoint

Code Comparison

react-intersection-observer:

import { useInView } from 'react-intersection-observer';

const Component = () => {
  const [ref, inView] = useInView({ threshold: 0.5 });
  return <div ref={ref}>{inView ? 'In view' : 'Not in view'}</div>;
};

react-waypoint:

import Waypoint from 'react-waypoint';

const Component = () => {
  return (
    <Waypoint onEnter={() => console.log('Entered')} onLeave={() => console.log('Left')}>
      <div>Waypoint content</div>
    </Waypoint>
  );
};

Both libraries provide React components for detecting when an element enters or leaves the viewport. react-intersection-observer offers a more modern approach with better performance for multiple instances, while react-waypoint has a simpler API and works out of the box in older browsers. The choice between them depends on your specific project requirements and browser support needs.

🔎 🏞 The original medium.com-inspired image zooming library for React (since 2016)

Pros of react-medium-image-zoom

  • Specifically designed for image zooming functionality
  • Provides a smooth and intuitive zoom experience
  • Customizable zoom styles and behaviors

Cons of react-medium-image-zoom

  • Limited to image zooming, less versatile than react-waypoint
  • May require additional setup for complex image layouts
  • Potentially heavier performance impact for large image galleries

Code Comparison

react-medium-image-zoom:

import Zoom from 'react-medium-image-zoom'

<Zoom>
  <img src="image.jpg" alt="Zoomable image" />
</Zoom>

react-waypoint:

import Waypoint from 'react-waypoint'

<Waypoint onEnter={this.handleWaypointEnter}>
  <div>Content to trigger waypoint</div>
</Waypoint>

While react-medium-image-zoom focuses on enhancing image viewing experiences with zoom functionality, react-waypoint is a more general-purpose tool for detecting when an element enters or leaves the viewport. react-medium-image-zoom is ideal for projects requiring image zoom features, whereas react-waypoint is better suited for implementing infinite scrolling, lazy loading, or triggering animations based on scroll position. The choice between these libraries depends on the specific requirements of your project and the desired user interactions.

It is a trap! A lock for a Focus. 🔓

Pros of react-focus-lock

  • Specifically designed for managing focus within modal dialogs and other UI components
  • Provides a more comprehensive solution for accessibility and keyboard navigation
  • Supports nested focus trapping scenarios

Cons of react-focus-lock

  • More complex API compared to react-waypoint's simpler implementation
  • May have a slightly larger bundle size due to additional functionality
  • Requires more setup and configuration for basic use cases

Code Comparison

react-focus-lock:

import FocusLock from 'react-focus-lock';

<FocusLock>
  <Modal>
    <button>Focusable element</button>
  </Modal>
</FocusLock>

react-waypoint:

import Waypoint from 'react-waypoint';

<Waypoint
  onEnter={this.handleWaypointEnter}
  onLeave={this.handleWaypointLeave}
>
  <div>Waypoint content</div>
</Waypoint>

Summary

While react-focus-lock is specifically designed for managing focus and improving accessibility, react-waypoint is primarily used for detecting when an element enters or leaves the viewport. react-focus-lock offers more comprehensive focus management features, but may be overkill for simpler use cases. react-waypoint has a simpler API and is better suited for scroll-based interactions and lazy loading. The choice between the two depends on the specific requirements of your project, with react-focus-lock being more suitable for complex modal dialogs and accessibility concerns, and react-waypoint for scroll-based interactions and performance optimization.

📏 Compute measurements of a React component.

Pros of react-measure

  • Provides more detailed measurements, including width, height, top, left, right, and bottom
  • Supports measuring content, padding, border, and margin separately
  • Offers a more flexible API with options for debouncing and scroll containers

Cons of react-measure

  • Slightly more complex to set up and use compared to react-waypoint
  • May have a higher performance impact due to more frequent measurements
  • Less focused on scroll-based events, which is react-waypoint's primary use case

Code Comparison

react-measure:

<Measure
  bounds
  onResize={contentRect => {
    this.setState({ dimensions: contentRect.bounds })
  }}
>
  {({ measureRef }) => <div ref={measureRef}>Your content here</div>}
</Measure>

react-waypoint:

<Waypoint
  onEnter={this.handleWaypointEnter}
  onLeave={this.handleWaypointLeave}
>
  <div>Your content here</div>
</Waypoint>

Summary

react-measure is more versatile for general element measurements, while react-waypoint excels in scroll-based detection. Choose react-measure for detailed size and position tracking, and react-waypoint for simpler scroll-based interactions.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

React Waypoint

npm version Build Status

A React component to execute a function whenever you scroll to an element. Works in all containers that can scroll, including the window.

React Waypoint can be used to build features like lazy loading content, infinite scroll, scrollspies, or docking elements to the viewport on scroll.

Inspired by Waypoints, except this little library grooves the React way.

Demo

Demo of React Waypoint in action

View demo page

Installation

npm

npm install react-waypoint --save

yarn

yarn add react-waypoint

Usage

import { Waypoint } from 'react-waypoint';

<Waypoint
  onEnter={this._handleWaypointEnter}
  onLeave={this._handleWaypointLeave}
/>

A waypoint normally fires onEnter and onLeave as you are scrolling, but it can fire because of other events too:

  • When the window is resized
  • When it is mounted (fires onEnter if it's visible on the page)
  • When it is updated/re-rendered by its parent

Callbacks will only fire if the new position changed from the last known position. Sometimes it's useful to have a waypoint that fires onEnter every time it is updated as long as it stays visible (e.g. for infinite scroll). You can then use a key prop to control when a waypoint is reused vs. re-created.

<Waypoint
  key={cursor}
  onEnter={this._loadMoreContent}
/>

Alternatively, you can also use an onPositionChange event to just get notified when the waypoint's position (e.g. inside the viewport, above or below) has changed.

<Waypoint
  onPositionChange={this._handlePositionChange}
/>

Waypoints can take a child, allowing you to track when a section of content enters or leaves the viewport. For details, see Children, below.

<Waypoint onEnter={this._handleEnter}>
  <div>
    Some content here
  </div>
</Waypoint>

Example: JSFiddle Example

Prop types

  propTypes: {

    /**
     * Function called when waypoint enters viewport
     */
    onEnter: PropTypes.func,

    /**
     * Function called when waypoint leaves viewport
     */
    onLeave: PropTypes.func,

    /**
     * Function called when waypoint position changes
     */
    onPositionChange: PropTypes.func,

    /**
     * Whether to activate on horizontal scrolling instead of vertical
     */
    horizontal: PropTypes.bool,

    /**
     * `topOffset` can either be a number, in which case its a distance from the
     * top of the container in pixels, or a string value. Valid string values are
     * of the form "20px", which is parsed as pixels, or "20%", which is parsed
     * as a percentage of the height of the containing element.
     * For instance, if you pass "-20%", and the containing element is 100px tall,
     * then the waypoint will be triggered when it has been scrolled 20px beyond
     * the top of the containing element.
     */
    topOffset: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number,
    ]),

    /**
     * `bottomOffset` can either be a number, in which case its a distance from the
     * bottom of the container in pixels, or a string value. Valid string values are
     * of the form "20px", which is parsed as pixels, or "20%", which is parsed
     * as a percentage of the height of the containing element.
     * For instance, if you pass "20%", and the containing element is 100px tall,
     * then the waypoint will be triggered when it has been scrolled 20px beyond
     * the bottom of the containing element.
     * 
     * Similar to `topOffset`, but for the bottom of the container.
     */
    bottomOffset: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number,
    ]),

    /**
     * Scrollable Ancestor - A custom ancestor to determine if the
     * target is visible in it. This is useful in cases where
     * you do not want the immediate scrollable ancestor to be
     * the container. For example, when your target is in a div
     * that has overflow auto but you are detecting onEnter based
     * on the window.
     *
     * This should typically be a reference to a DOM node, but it will also work
     * to pass it the string "window" if you are using server rendering.
     */
    scrollableAncestor: PropTypes.any,

    /**
     * fireOnRapidScroll - if the onEnter/onLeave events are to be fired
     * on rapid scrolling. This has no effect on onPositionChange -- it will
     * fire anyway.
     */
    fireOnRapidScroll: PropTypes.bool,

    /**
     * Use this prop to get debug information in the console log. This slows
     * things down significantly, so it should only be used during development.
     */
    debug: PropTypes.bool,
  },

All callbacks (onEnter/onLeave/onPositionChange) receive an object as the only argument. That object has the following properties:

  • currentPosition - the position that the waypoint has at the moment. One of Waypoint.below, Waypoint.above, Waypoint.inside, and Waypoint.invisible.
  • previousPosition - the position that the waypoint had before. Also one of Waypoint.below, Waypoint.above, Waypoint.inside, and Waypoint.invisible.

In most cases, the above two properties should be enough. In some cases though, you might find these additional properties useful:

  • event - the native scroll event that triggered the callback. May be missing if the callback wasn't triggered as the result of a scroll.
  • waypointTop - the waypoint's distance to the top of the viewport.
  • viewportTop - the distance from the scrollable ancestor to the viewport top.
  • viewportBottom - the distance from the bottom of the scrollable ancestor to the viewport top.

If you use es6 object destructuring, this means that you can use waypoints in the following way:

<Waypoint onEnter={({ previousPosition, currentPosition, event }) => {
    // do something useful!
  }}
/>

If you are more familiar with plain old js functions, you'll do something like this:

<Waypoint onEnter={function(props) {
    // here you can use `props.currentPosition`, `props.previousPosition`, and
    // `props.event`
  }}
/>

Offsets and Boundaries

Two of the Waypoint props are topOffset and bottomOffset. To appreciate what these can do for you, it will help to have an understanding of the "boundaries" used by this library. The boundaries of React Waypoint are the top and bottom of the element containing your scrollable content (although this element can be configured). When a waypoint is within these boundaries, it is considered to be "inside." When a waypoint passes beyond these boundaries, then it is "outside." The onEnter and onLeave props are called as an element transitions from being inside to outside, or vice versa.

The topOffset and bottomOffset properties can adjust the placement of these boundaries. By default, the offset is '0px'. If you specify a positive value, then the boundaries will be pushed inward, toward the center of the page. If you specify a negative value for an offset, then the boundary will be pushed outward from the center of the page.

Here is an illustration of offsets and boundaries. The black box is the scrollableAncestor. The pink lines represent the location of the boundaries. The offsets that determine the boundaries are in light pink.

Horizontal Scrolling Offsets and Boundaries

By default, waypoints listen to vertical scrolling. If you want to switch to horizontal scrolling instead, use the horizontal prop. For simplicity's sake, all other props and callbacks do not change. Instead, topOffset and bottomOffset (among other directional variables) will mean the offset from the left and the offset from the right, respectively, and work exactly as they did before, just calculated in the horizontal direction.

Example Usage

Positive values of the offset props are useful when you have an element that overlays your scrollable area. For instance, if your app has a 50px fixed header, then you may want to specify topOffset='50px', so that the onEnter callback is called when waypoints scroll into view from beneath the header.

Negative values of the offset prop could be useful for lazy loading. Imagine if you had a lot of large images on a long page, but you didn't want to load them all at once. You can use React Waypoint to receive a callback whenever an image is a certain distance from the bottom of the page. For instance, by specifying bottomOffset='-200px', then your onEnter callback would be called when the waypoint comes closer than 200 pixels from the bottom edge of the page. By placing a waypoint near each image, you could dynamically load them.

There are likely many more use cases for the offsets: be creative! Also, keep in mind that there are two boundaries, so there are always two positions when the onLeave and onEnter callback will be called. By using the arguments passed to the callbacks, you can determine whether the waypoint has crossed the top boundary or the bottom boundary.

Children

If you don't pass a child into your Waypoint, then you can think of the waypoint as a line across the page. Whenever that line crosses a boundary, then the onEnter or onLeave callbacks will be called.

If you do pass a child, it can be a single DOM component (e.g. <div>) or a composite component (e.g. <MyComponent />).

Waypoint needs a DOM node to compute its boundaries. When you pass a DOM component to Waypoint, it handles getting a reference to the DOM node through the ref prop automatically.

If you pass a composite component, you can wrap it with React.forwardRef (requires react@^16.3.0) and have the ref prop being handled automatically for you, like this:

class Block extends React.Component {
  render() {
    return <div ref={this.props.innerRef}>Hello</div>
  }
}

const BlockWithRef = React.forwardRef((props, ref) => {
  return <Block innerRef={ref} {...props} />
})

const App = () => (
  <Waypoint>
    <BlockWithRef />
  </Waypoint>
)

If you can't do that because you are using older version of React then you need to make use of the innerRef prop passed by Waypoint to your component. Simply pass it through as the ref of a DOM component and you're all set. Like in this example:

class Block extends React.Component {
  render() {
    return <div ref={this.props.innerRef}>Hello</div>
  }
}
Block.propTypes = {
  innerRef: PropTypes.func.isRequired,
}

const App = () => (
  <Waypoint>
    <Block />
  </Waypoint>
)

The onEnter callback will be called when any part of the child is visible in the viewport. The onLeave callback will be called when all of the child has exited the viewport.

(Note that this is measured only on a single axis. What this means is that for a Waypoint within a vertically scrolling parent, it could be off of the screen horizontally yet still fire an onEnter event, because it is within the vertical boundaries).

Deciding whether to pass a child or not will depend on your use case. One example of when passing a child is useful is for a scrollspy (like Bootstrap's). Imagine if you want to fire a waypoint when a particularly long piece of content is visible onscreen. When the page loads, it is conceivable that both the top and bottom of this piece of content could lie outside of the boundaries, because the content is taller than the viewport. If you didn't pass a child, and instead put the waypoint above or below the content, then you will not receive an onEnter callback (nor any other callback from this library). Instead, passing this long content as a child of the Waypoint would fire the onEnter callback when the page loads.

Containing elements and scrollableAncestor

React Waypoint positions its boundaries based on the first scrollable ancestor of the Waypoint.

If that algorithm doesn't work for your use case, then you might find the scrollableAncestor prop useful. It allows you to specify what the scrollable ancestor is. Pass a reference to a DOM node as that prop, and the Waypoint will use the scroll position of that node, rather than its first scrollable ancestor.

This can also be the string "window", which can be useful if you are using server rendering.

Example Usage

Sometimes, waypoints that are deeply nested in the DOM tree may need to track the scroll position of the page as a whole. If you want to be sure that no other scrollable ancestor is used (since, once again, the first scrollable ancestor is what the library will use by default), then you can explicitly set the scrollableAncestor to be the window to ensure that no other element is used.

This might look something like:

<Waypoint
  scrollableAncestor={window}
  onEnter={this._handleWaypointEnter}
  onLeave={this._handleWaypointLeave}
/>

Troubleshooting

If your waypoint isn't working the way you expect it to, there are a few ways you can debug your setup.

OPTION 1: Add the debug={true} prop to your waypoint. When you do, you'll see console logs informing you about the internals of the waypoint.

OPTION 2: Clone and modify the project locally.

  • clone this repo
  • add console.log or breakpoints where you think it would be useful.
  • npm link in the react-waypoint repo.
  • npm link react-waypoint in your project.
  • if needed rebuild react-waypoint module: npm run build-npm

Limitations

In this component we make a few assumptions that we believe are generally safe, but in some situations might present limitations.

  • We determine the scrollable-ness of a node by inspecting its computed overflow-y or overflow property and nothing else. This could mean that a container with this style that does not actually currently scroll will be considered when performing visibility calculations.
  • We assume that waypoints are rendered within at most one scrollable container. If you render a waypoint in multiple nested scrollable containers, the visibility calculations will likely not be accurate.
  • We also base the visibility calculations on the scroll position of the scrollable container (or window if no scrollable container is found). This means that if your scrollable container has a height that is greater than the window, it might trigger onEnter unexpectedly.

In the wild

Organizations and projects using react-waypoint.

Credits

Credit to trotzig and lencioni for writing this component, and Brigade for open sourcing it.

Thanks to the creator of the original Waypoints library, imakewebthings.

License

MIT


Make sure to check out other popular open-source tools from the Brigade team: dock, overcommit, haml-lint, and scss-lint.

NPM DownloadsLast 30 Days