Infinite Scrolling with Zero JavaScript [Rails]
Published: 2023-09-23 17:50:01

---
description: Infinite scrolling is a web design technique that loads content continuously as the user scrolls down the page. In this guide, we'll use two Turbo Frames to accomplish this.
---

Infinite scrolling is a web design technique that loads content continuously as the user scrolls down the page, eliminating the need for pagination. It is a great way to improve user experience, providing a seamless way for users to consume content. In this article, we'll explore how to implement this feature in a Ruby on Rails application without writing a single line of JavaScript, thanks to the Hotwire library.


![A GIF illustrating a thumb scrolling a smartphone infinitely](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c8n9xwjwguufis8dyhf2.gif)

With [Hotwire](https://hotwired.dev/), we can leverage [Turbo Frames](https://turbo.hotwired.dev/handbook/frames) and [Streams](https://turbo.hotwired.dev/handbook/streams) to facilitate partial page updates, enhancing our user experience with very little code.

## The Scenario
Imagine we are building a feature to display a list of user comments on a blog post. We want to continuously load these comments as the user scrolls, eliminating the need for them to click through pages. To achieve this, all we need is two turbo frames:
```erb
<%= turbo_frame_tag(:comments_container) do %>
  <%= turbo_frame_tag([:comments, 1], src: comments_path(page_size: 20)) do %>
    <!-- Skeleton Loading Indicator -->
    <div class="flex items-center space-x-4 animate-pulse">
      <div class="rounded-full bg-gray-200 h-8 w-8"></div>
      <div class="flex-1 space-y-2">
        <div class="h-8 bg-gray-200 rounded w-1/2"></div>
      </div>
    </div>
  <% end %>
<% end %>
```

This code sets up two turbo frames. The :comments_container is where we will be appending our lazy loaded page results, and the inner `:comments, 1` frame is where we initially load the first page of comments. It will have an `id` in the DOM of `comments_1`.

Next, we need to write the response from our comments controller. For each page we load, we need to render the comments for the current page. Crucially, we also need to set up the turbo_frame_tag to lazy load the next page of results (or render a message if there are no more results to fetch).
```erb
<%= turbo_frame_tag([:comments, @page]) do %>
  <!-- Loop through each comment and render it on the page -->
  <% @comments.each do |comment| %>
    <%= render comment %>
  <% end %>

  <%= turbo_stream.append :comments_container do %>
    <!-- Check if there are more pages to load -->
    <% if @page < @total_pages %>
      <!-- Set up the next frame to load when it comes into view -->
      <%= turbo_frame_tag([:comments, @page + 1], src: comments_path(page_size: 20, page: @page + 1), loading: :lazy) do %>
        <!-- Skeleton Loading Indicator -->
        <div class="flex items-center space-x-4 animate-pulse">
          <div class="rounded-full bg-gray-200 h-8 w-8"></div>
          <div class="flex-1 space-y-2">
            <div class="h-8 bg-gray-200 rounded w-1/2"></div>
          </div>
        </div>
      <% end %>
    <% else %>
      <!-- Display a message if there are no more comments to load -->
      <%= @page == 1 && @comments.empty? ? "No comments yet" : "You've reached the end" %>
    <% end %>
  <% end %>
<% end %>
```

After a bit of scrolling in the browser, if everything is working our DOM should be structured like this:
```HTML
<turbo-frame id="comments_container">
  <turbo-frame id="comments_1">
  <turbo-frame id="comments_2">
  <turbo-frame id="comments_3">
  <!-- ... -->
</turbo-frame>
```

## Implementation Highlights
### 1. Lazy Loading:
Imagine you're reading a book, and the next page only appears when you’re ready to read it. That's what `loading: :lazy` does; it only loads the next set of comments when the user is ready to read them, i.e., when they come into view.

### 2. Skeleton Loading Indicator:
You know when you load a video and see a blurry gray blob before it fully loads? That's what the skeleton loading indicator does; it provides a visual hint that more content is on its way.

### 3. Smooth Sailing with Zero JavaScript:
The cool part? We achieved this modern user experience with just a few lines of code, without writing any JavaScript, using turbo frame tags instead.

## Conclusion
By leveraging the Hotwire library in Ruby on Rails, implementing infinite scrolling is like a JavaScript-free walk in the park. It's neat, efficient, and developer-friendly. I hope this technique becomes a useful tool in your developer toolbox!