Create an infinite scrolling blog roll in Rails with Hotwire
In this tutorial, I’ll show you how to add an infinitely scrolling blog roll using Rails and Hotwire. Note that this is different than Chris Oliver’s awesome infinite scroll tutorial, in that we’re loading a new post once a user scrolls to the bottom of a current post. Below is a demo.
Step 1: Application Set-Up
rails new rails-infinite-scroll-posts -d-postgresql --webpacker=stimulusrails db:setupbundle add turbo-railsrails turbo:install
Step 2: Create Post Scaffold
rails g scaffold post title body:textrails db:migrate
Step 3: Add Seed Data
bundle add faker -g=development-
Update
db/seeds.rb rails db:seed
Step 4. Create the ability to navigate between Posts
-
touch app/models/concerns/navigable.rb -
Include Module in Post Model
Note: We could just add the
nextandpreviousmethods directly in thePostmodel, but using a Module means we can use these methods in future models. -
Update PostsController
Step 5: Use Turbo Frames to lazy-load the next Post
-
Add frames to
app/views/posts/show.html.erb
What’s going on?
- We wrap the content in a
turbo_frame_tagwith anIDofdom_id(@post). For example, thedom_id(@post)call will evaluate toid="post_1"if the Post’s ID is 1. This keeps the ID’s unique. - We add another
turbo_frame_tagwithin the outerturbo_frame_tagto lazy-load the next post. We can look for the next post thanks to ourNavigablemodule that we created earlier.- The
loadingattribute ensures that the frame will only load once it appears in the viewport.
- The
- We add
data: { turbo_frame: "_top" }to override navigation targets and force those pages to replace the whole frame. Otherwise, we would need to add Turbo Frames to theeditandindexviews.- This is only because those links are nested in the outermost
turbo_frame_tag.
- This is only because those links are nested in the outermost
Step 6: Use Stimulus to update the path as new posts are loaded
-
touch app/javascript/controllers/infinite_scroll_controller.js -
Update that markup in
app/views/posts/show.html.erb
What’s going on?
- We use the Intersection Observer API to determine when the post has entered the viewport.
- We set the
thresholdto[0, 1.0]to account for elements that are taller than the viewport. This ensures thatentry.isIntersectingwill returntrue.
- We set the
- When
entry.isIntersectingreturnstrue, we use History.replaceState() to update the URL with the path for the post that entered the viewport.- The value for the path is stored in the
data-infinite-scroll-path-valueattribute. - We add
history.stateas the first argument tohistory.replaceStateto account for an issue with Turbolinks.
- The value for the path is stored in the
Step 7: Add a loading state and styles (optional)
-
Add Bootstrap via CDN to
app/views/layouts/application.html.erb -
Update markup and add a loader to
app/views/posts/show.html.erb
