Infinite Scroll done the Right way
By: Jose Granja
Tags: React, Javascript Tips, JavaScript, Web Development, React Hook

Infinite Scroll done the Right way

infinite scrolling made simple and performant

As most of you know Infinite scrolling is a web-design technique that loads content continuously as the user scrolls down the page. This approach does eliminates the need for any pagination elements. It frees the User from that burden leading to a clean UX experience. You can find this technique implementation in the most popular social media feeds.

The most obvious benefit is that only the relevant information is being loaded as the user needs it. This reduces the size of initial request data and gives the chance to progressively render the elements as they are needed. This technique comes very much hand in hand with list virtualisation but that’s a topic for another article. Here I just want to focus on how to make that infinite scroll happen.

What we will do together is look at the traditional goto implementation and see how that can be improved. I will be writing the example with react hooks but this applies to any UI framework, it can easily done with vanilla Javascript.

Most common approach

If you are to search in the web you’ll see that the most common approach is to rely on the browser scroll event. You can set a handle to check the scroll’s position and see if that lands before or after you last element. With that information you can decide whether to fire the infinite scroll callback or not.

What we do is define an element at the end of the list and check if the user scrolled to that last item to trigger the loading of new elements.

Here is an example of how that list would be rendered

Let’s start by defining our hasReachedBottom function

as you can see this particular implementation relies of the element being the last item on the page. It’s not super relevant and there’s other ways of doing that calculation for you specific scenario.

Let’s now put that function to work:

as you can see what we are doing is hooking up to the scroll event and

check that there’s not a current fetch happening check if the element has reached the bottom do the fetch

Note the offset variable: we are using an offset just to anticipate when the last element will become visible. This is handy if you want to load the content before the User reaches the last element. If the content loads fast it will be a flawless experience.

One other important note is to always clean after yourself. When using useEffect you can return a function which is in charge of the cleanup. In this case we need to clean the scroll event handler otherwise we will run into memory leaks

Although this approach is quite easy, it is not the most efficient one: on each user scroll event we are manually checking if he reached the bottom of the list. We can be much more efficient than that.

IntersectionObserver to the rescue

Not very long ago the IntersectionObserver feature was released. What this enable us to do is to reactively observe elements and get events or when those element become visible or hidden on the viewport.

If you read the mdn documentation here you’ll find that one of the recommended use cases is indeed the infinite scroll feature. Let’s see how we can implement this with react and hooks:

You can see how this leads to a much cleaner implementation. We don’t have to deal on an imperative way on how to know if the user reached that last element: the browser simply does notify us when there’s a change on visibility. The browser is doing the heavy lifting for us :) This implementation leads to having less events being delegated to us and therefore a much more performant implementation.

Note that the approach we did before with the offset is very much possible here. IntersectionObserver takes a second argument which is a set of options. You can use rootMargin: -100px if you want to anticipate the user landing on that element.

Support for IntersectionObserver

The support for this feature is pretty great: 92.3%

If you are worried about those 7.7% of users, don’t panic. you can just use:

This polyfill tool will only load the fallback implementation in case the browser does need it.

hope you enjoyed the article and learned something new

Cheers 🍻

update: I wrote another article about the IntersectionObserver, check it out if you want to keep learning about it: