{"id":26690765,"url":"https://github.com/brigade/react-waypoint","last_synced_at":"2025-03-26T16:01:05.419Z","repository":{"id":26430691,"uuid":"29881215","full_name":"civiccc/react-waypoint","owner":"civiccc","description":"A React component to execute a function whenever you scroll to an element.","archived":false,"fork":false,"pushed_at":"2022-06-08T15:28:16.000Z","size":649,"stargazers_count":4071,"open_issues_count":62,"forks_count":209,"subscribers_count":76,"default_branch":"master","last_synced_at":"2025-03-20T08:32:36.412Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/civiccc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-26T20:56:40.000Z","updated_at":"2025-03-19T14:37:07.000Z","dependencies_parsed_at":"2022-07-12T10:49:24.711Z","dependency_job_id":null,"html_url":"https://github.com/civiccc/react-waypoint","commit_stats":null,"previous_names":["brigade/react-waypoint"],"tags_count":73,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civiccc%2Freact-waypoint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civiccc%2Freact-waypoint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civiccc%2Freact-waypoint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civiccc%2Freact-waypoint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/civiccc","download_url":"https://codeload.github.com/civiccc/react-waypoint/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245689494,"owners_count":20656416,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-26T16:00:36.059Z","updated_at":"2025-03-26T16:01:05.388Z","avatar_url":"https://github.com/civiccc.png","language":"JavaScript","readme":"# React Waypoint\n\n[![npm version](https://badge.fury.io/js/react-waypoint.svg)](http://badge.fury.io/js/react-waypoint)\n[![Build Status](https://travis-ci.org/civiccc/react-waypoint.svg?branch=master)](https://travis-ci.org/civiccc/react-waypoint)\n\nA React component to execute a function whenever you scroll to an element. Works\nin all containers that can scroll, including the window.\n\nReact Waypoint can be used to build features like lazy loading content, infinite\nscroll, scrollspies, or docking elements to the viewport on scroll.\n\nInspired by [Waypoints][waypoints], except this little library grooves the\n[React][react] way.\n\n## Demo\n![Demo of React Waypoint in action](https://raw.github.com/civiccc/react-waypoint/master/react-waypoint-demo.gif)\n\n[View demo page][demo-page]\n\n[waypoints]: https://github.com/imakewebthings/waypoints\n[react]: https://github.com/facebook/react\n[demo-page]: https://civiccc.github.io/react-waypoint/\n\n## Installation\n\n### npm\n\n```bash\nnpm install react-waypoint --save\n```\n\n### yarn\n\n```bash\nyarn add react-waypoint\n```\n\n## Usage\n\n```jsx\nimport { Waypoint } from 'react-waypoint';\n\n\u003cWaypoint\n  onEnter={this._handleWaypointEnter}\n  onLeave={this._handleWaypointLeave}\n/\u003e\n```\n\nA waypoint normally fires `onEnter` and `onLeave` as you are scrolling, but it\ncan fire because of other events too:\n\n- When the window is resized\n- When it is mounted (fires `onEnter` if it's visible on the page)\n- When it is updated/re-rendered by its parent\n\nCallbacks will only fire if the new position changed from the last known\nposition. Sometimes it's useful to have a waypoint that fires `onEnter` every\ntime it is updated as long as it stays visible (e.g. for infinite scroll). You\ncan then use a `key` prop to control when a waypoint is reused vs. re-created.\n\n```jsx\n\u003cWaypoint\n  key={cursor}\n  onEnter={this._loadMoreContent}\n/\u003e\n```\n\nAlternatively, you can also use an `onPositionChange` event to just get\nnotified when the waypoint's position (e.g. inside the viewport, above or\nbelow) has changed.\n\n```jsx\n\u003cWaypoint\n  onPositionChange={this._handlePositionChange}\n/\u003e\n```\n\nWaypoints can take a child, allowing you to track when a section of content\nenters or leaves the viewport. For details, see [Children](#children), below.\n\n```jsx\n\u003cWaypoint onEnter={this._handleEnter}\u003e\n  \u003cdiv\u003e\n    Some content here\n  \u003c/div\u003e\n\u003c/Waypoint\u003e\n```\n\n### Example: [JSFiddle Example][jsfiddle-example]\n\n[jsfiddle-example]: http://jsfiddle.net/L4z5wcx0/7/\n\n## Prop types\n\n```jsx\n  propTypes: {\n\n    /**\n     * Function called when waypoint enters viewport\n     */\n    onEnter: PropTypes.func,\n\n    /**\n     * Function called when waypoint leaves viewport\n     */\n    onLeave: PropTypes.func,\n\n    /**\n     * Function called when waypoint position changes\n     */\n    onPositionChange: PropTypes.func,\n\n    /**\n     * Whether to activate on horizontal scrolling instead of vertical\n     */\n    horizontal: PropTypes.bool,\n\n    /**\n     * `topOffset` can either be a number, in which case its a distance from the\n     * top of the container in pixels, or a string value. Valid string values are\n     * of the form \"20px\", which is parsed as pixels, or \"20%\", which is parsed\n     * as a percentage of the height of the containing element.\n     * For instance, if you pass \"-20%\", and the containing element is 100px tall,\n     * then the waypoint will be triggered when it has been scrolled 20px beyond\n     * the top of the containing element.\n     */\n    topOffset: PropTypes.oneOfType([\n      PropTypes.string,\n      PropTypes.number,\n    ]),\n\n    /**\n     * `bottomOffset` can either be a number, in which case its a distance from the\n     * bottom of the container in pixels, or a string value. Valid string values are\n     * of the form \"20px\", which is parsed as pixels, or \"20%\", which is parsed\n     * as a percentage of the height of the containing element.\n     * For instance, if you pass \"20%\", and the containing element is 100px tall,\n     * then the waypoint will be triggered when it has been scrolled 20px beyond\n     * the bottom of the containing element.\n     * \n     * Similar to `topOffset`, but for the bottom of the container.\n     */\n    bottomOffset: PropTypes.oneOfType([\n      PropTypes.string,\n      PropTypes.number,\n    ]),\n\n    /**\n     * Scrollable Ancestor - A custom ancestor to determine if the\n     * target is visible in it. This is useful in cases where\n     * you do not want the immediate scrollable ancestor to be\n     * the container. For example, when your target is in a div\n     * that has overflow auto but you are detecting onEnter based\n     * on the window.\n     *\n     * This should typically be a reference to a DOM node, but it will also work\n     * to pass it the string \"window\" if you are using server rendering.\n     */\n    scrollableAncestor: PropTypes.any,\n\n    /**\n     * fireOnRapidScroll - if the onEnter/onLeave events are to be fired\n     * on rapid scrolling. This has no effect on onPositionChange -- it will\n     * fire anyway.\n     */\n    fireOnRapidScroll: PropTypes.bool,\n\n    /**\n     * Use this prop to get debug information in the console log. This slows\n     * things down significantly, so it should only be used during development.\n     */\n    debug: PropTypes.bool,\n  },\n```\n\nAll callbacks (`onEnter`/`onLeave`/`onPositionChange`) receive an object as the\nonly argument. That object has the following properties:\n\n- `currentPosition` - the position that the waypoint has at the moment. One\n  of `Waypoint.below`, `Waypoint.above`, `Waypoint.inside`,\n  and `Waypoint.invisible`.\n- `previousPosition` - the position that the waypoint had before. Also one\n  of `Waypoint.below`, `Waypoint.above`, `Waypoint.inside`,\n  and `Waypoint.invisible`.\n\nIn most cases, the above two properties should be enough. In some cases\nthough, you might find these additional properties useful:\n\n- `event` - the native [scroll\n  event](https://developer.mozilla.org/en-US/docs/Web/Events/scroll) that\n  triggered the callback. May be missing if the callback wasn't triggered\n  as the result of a scroll.\n- `waypointTop` - the waypoint's distance to the top of the viewport.\n- `viewportTop` - the distance from the scrollable ancestor to the\n  viewport top.\n- `viewportBottom` - the distance from the bottom of the scrollable\n  ancestor to the viewport top.\n\nIf you use [es6 object\ndestructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment),\nthis means that you can use waypoints in the following way:\n\n```jsx\n\u003cWaypoint onEnter={({ previousPosition, currentPosition, event }) =\u003e {\n    // do something useful!\n  }}\n/\u003e\n```\n\nIf you are more familiar with plain old js functions, you'll do something like\nthis:\n\n```jsx\n\u003cWaypoint onEnter={function(props) {\n    // here you can use `props.currentPosition`, `props.previousPosition`, and\n    // `props.event`\n  }}\n/\u003e\n```\n\n## Offsets and Boundaries\n\nTwo of the Waypoint props are `topOffset` and `bottomOffset`. To appreciate\nwhat these can do for you, it will help to have an understanding of the\n\"boundaries\" used by this library. The boundaries of React Waypoint are the top\nand bottom of the element containing your scrollable content ([although this element\ncan be configured](#containing-elements-and-scrollableancestor)). When a\nwaypoint is within these boundaries, it is considered to be \"inside.\" When a\nwaypoint passes beyond these boundaries, then it is \"outside.\" The `onEnter` and\n`onLeave` props are called as an element transitions from being inside to\noutside, or vice versa.\n\nThe `topOffset` and `bottomOffset` properties can adjust the placement of these\nboundaries. By default, the offset is `'0px'`. If you specify a positive value,\nthen the boundaries will be pushed inward, toward the center of the page. If\nyou specify a negative value for an offset, then the boundary will be pushed\noutward from the center of the page.\n\nHere is an illustration of offsets and boundaries. The black box is the\n[`scrollableAncestor`](#containing-elements-and-scrollableancestor). The pink\nlines represent the location of the boundaries. The offsets that determine\nthe boundaries are in light pink.\n\n![](https://cloud.githubusercontent.com/assets/2322305/16939123/5be12454-4d33-11e6-86b6-ad431da93bf2.png)\n\n#### Horizontal Scrolling Offsets and Boundaries\n\nBy default, waypoints listen to vertical scrolling. If you want to switch to\nhorizontal scrolling instead, use the `horizontal` prop. For simplicity's sake,\nall other props and callbacks do not change. Instead, `topOffset` and\n`bottomOffset` (among other directional variables) will mean the offset from\nthe left and the offset from the right, respectively, and work exactly as they\ndid before, just calculated in the horizontal direction.\n\n#### Example Usage\n\nPositive values of the offset props are useful when you have an element that\noverlays your scrollable area. For instance, if your app has a `50px` fixed\nheader, then you may want to specify `topOffset='50px'`, so that the\n`onEnter` callback is called when waypoints scroll into view from beneath the\nheader.\n\nNegative values of the offset prop could be useful for lazy loading. Imagine if\nyou had a lot of large images on a long page, but you didn't want to load them\nall at once. You can use React Waypoint to receive a callback whenever an image\nis a certain distance from the bottom of the page. For instance, by specifying\n`bottomOffset='-200px'`, then your `onEnter` callback would be called when\nthe waypoint comes closer than 200 pixels from the bottom edge of the page. By\nplacing a waypoint near each image, you could dynamically load them.\n\nThere are likely many more use cases for the offsets: be creative! Also, keep in\nmind that there are _two_ boundaries, so there are always _two_ positions when\nthe `onLeave` and `onEnter` callback will be called. By using the arguments\npassed to the callbacks, you can determine whether the waypoint has crossed the\ntop boundary or the bottom boundary.\n\n## Children\n\nIf you don't pass a child into your Waypoint, then you can think of the\nwaypoint as a line across the page. Whenever that line crosses a\n[boundary](#offsets-and-boundaries), then the `onEnter` or `onLeave` callbacks\nwill be called.\n\nIf you do pass a child, it can be a single DOM component (e.g. `\u003cdiv\u003e`) or a\ncomposite component (e.g. `\u003cMyComponent /\u003e`).\n\nWaypoint needs a DOM node to compute its boundaries. When you pass a DOM\ncomponent to Waypoint, it handles getting a reference to the DOM node through\nthe `ref` prop automatically.\n\nIf you pass a composite component, you can wrap it with `React.forwardRef` (requires `react@^16.3.0`)\nand have the `ref` prop being handled automatically for you, like this:\n\n```jsx\nclass Block extends React.Component {\n  render() {\n    return \u003cdiv ref={this.props.innerRef}\u003eHello\u003c/div\u003e\n  }\n}\n\nconst BlockWithRef = React.forwardRef((props, ref) =\u003e {\n  return \u003cBlock innerRef={ref} {...props} /\u003e\n})\n\nconst App = () =\u003e (\n  \u003cWaypoint\u003e\n    \u003cBlockWithRef /\u003e\n  \u003c/Waypoint\u003e\n)\n```\n\nIf you can't do that because you are using older version of React then\nyou need to make use of the `innerRef` prop passed by Waypoint to your component.\nSimply pass it through as the `ref` of a DOM component and you're all set. Like in\nthis example:\n\n```jsx\nclass Block extends React.Component {\n  render() {\n    return \u003cdiv ref={this.props.innerRef}\u003eHello\u003c/div\u003e\n  }\n}\nBlock.propTypes = {\n  innerRef: PropTypes.func.isRequired,\n}\n\nconst App = () =\u003e (\n  \u003cWaypoint\u003e\n    \u003cBlock /\u003e\n  \u003c/Waypoint\u003e\n)\n```\n\nThe `onEnter` callback will be called when *any* part of the child is visible\nin the viewport. The `onLeave` callback will be called when *all* of the child\nhas exited the viewport.\n\n(Note that this is measured only on a single axis. What this means is that for a\nWaypoint within a vertically scrolling parent, it could be off of the screen\nhorizontally yet still fire an onEnter event, because it is within the vertical\nboundaries).\n\nDeciding whether to pass a child or not will depend on your use case. One\nexample of when passing a child is useful is for a scrollspy\n(like [Bootstrap's](https://bootstrapdocs.com/v3.3.6/docs/javascript/#scrollspy)).\nImagine if you want to fire a waypoint when a particularly long piece of content\nis visible onscreen. When the page loads, it is conceivable that both the top\nand bottom of this piece of content could lie outside of the boundaries,\nbecause the content is taller than the viewport. If you didn't pass a child,\nand instead put the waypoint above or below the content, then you will not\nreceive an `onEnter` callback (nor any other callback from this library).\nInstead, passing this long content as a child of the Waypoint would fire the `onEnter`\ncallback when the page loads.\n\n## Containing elements and `scrollableAncestor`\n\nReact Waypoint positions its [boundaries](#offsets-and-boundaries) based on the\nfirst scrollable ancestor of the Waypoint.\n\nIf that algorithm doesn't work for your use case, then you might find the\n`scrollableAncestor` prop useful. It allows you to specify what the scrollable\nancestor is. Pass a reference to a DOM node as that prop, and the Waypoint will\nuse the scroll position of *that* node, rather than its first scrollable\nancestor.\n\nThis can also be the string \"window\", which can be useful if you are using\nserver rendering.\n\n#### Example Usage\n\nSometimes, waypoints that are deeply nested in the DOM tree may need to track\nthe scroll position of the page as a whole. If you want to be sure that no other\nscrollable ancestor is used (since, once again, the first scrollable ancestor is\nwhat the library will use by default), then you can explicitly set the\n`scrollableAncestor` to be the `window` to ensure that no other element is used.\n\nThis might look something like:\n\n```jsx\n\u003cWaypoint\n  scrollableAncestor={window}\n  onEnter={this._handleWaypointEnter}\n  onLeave={this._handleWaypointLeave}\n/\u003e\n```\n\n## Troubleshooting\n\nIf your waypoint isn't working the way you expect it to, there are a few ways\nyou can debug your setup.\n\nOPTION 1: Add the `debug={true}` prop to your waypoint. When you do, you'll see console\nlogs informing you about the internals of the waypoint.\n\nOPTION 2: Clone and modify the project locally.\n- clone this repo\n- add `console.log` or breakpoints where you think it would be useful.\n- `npm link` in the react-waypoint repo.\n- `npm link react-waypoint` in your project.\n- if needed rebuild react-waypoint module: `npm run build-npm`\n\n## Limitations\n\nIn this component we make a few assumptions that we believe are generally safe,\nbut in some situations might present limitations.\n\n- We determine the scrollable-ness of a node by inspecting its computed\n  overflow-y or overflow property and nothing else. This could mean that a\n  container with this style that does not actually currently scroll will be\n  considered when performing visibility calculations.\n- We assume that waypoints are rendered within at most one scrollable container.\n  If you render a waypoint in multiple nested scrollable containers, the\n  visibility calculations will likely not be accurate.\n- We also base the visibility calculations on the scroll position of the\n  scrollable container (or `window` if no scrollable container is found). This\n  means that if your scrollable container has a height that is greater than the\n  window, it might trigger `onEnter` unexpectedly.\n\n## In the wild\n\n[Organizations and projects using `react-waypoint`](INTHEWILD.md).\n\n## Credits\n\nCredit to [trotzig][trotzig-github] and [lencioni][lencioni-github] for writing\nthis component, and [Brigade][brigade-home] for open sourcing it.\n\nThanks to the creator of the original Waypoints library,\n[imakewebthings][imakewebthings-github].\n\n[lencioni-github]: https://github.com/lencioni\n[trotzig-github]: https://github.com/trotzig\n[brigade-home]: https://www.brigade.com/\n[imakewebthings-github]: https://github.com/imakewebthings\n\n## License\n\n[MIT][mit-license]\n\n[mit-license]: ./LICENSE\n\n---\n\n_Make sure to check out other popular open-source tools from the\n[Brigade][civiccc-github] team: [dock], [overcommit], [haml-lint], and [scss-lint]._\n\n[civiccc-github]: https://github.com/civiccc\n[dock]: https://github.com/civiccc/dock\n[overcommit]: https://github.com/sds/overcommit\n[haml-lint]: https://github.com/sds/haml-lint\n[scss-lint]: https://github.com/sds/scss-lint\n","funding_links":[],"categories":["UI Utilities","Uncategorized","UI Utilites","Libraries","JavaScript"],"sub_categories":["Reporter","Uncategorized","React"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrigade%2Freact-waypoint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrigade%2Freact-waypoint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrigade%2Freact-waypoint/lists"}