While Rails provides a nice way to delay the loading of images that do not yet appear on your screen, there is no built-in way to display a placeholder while waiting for the file to fully load.
This "lazy placeholding" technique is very practical because unless you already have very optimized files, image loading can make your UX slow and janky.
Simple and classic tools like Vanilla-Lazyload are here to help us! Here's how to quickly integrate it with Rails. LazyLoad also works with animated SVGs, videos and iframes but we'll be focusing on images.
Add the vanilla-lazyload package to your app. Personally, I'm a fan of importmap and the radical approach of "no build". But you can also install it using a CDN or npm.
bin/importmap pin vanilla-lazyload
Import LazyLoad in the Stimulus controller of your choice. In my case, I want to show a lightweight placeholder while waiting for my users' avatar to show up, so I'll do that in
users_controller.js
. You can also import it inapplication.js
if you're not using Stimulus for your JS.import { Controller } from "@hotwired/stimulus"; import LazyLoad from "vanilla-lazyload"; // Connects to data-controller="users" export default class extends Controller { connect() { new LazyLoad(); } }
Edit the image ERB tag so that a placeholder is displayed before the final image loads.
But first, note that LazyLoad does not have direct access to your assets, so you'll need to provide a full URL of the final image, using url_for(@user.avatar)
for example.
Then, replace the original image with the placeholder (placeholder.png
for example) and move it to data-src
. Don't forget to add the lazy
class to the image so that LazyLoad can perform the replacement operation !
In other words, this :
<%= image_tag @user.avatar, class:"w-8 h-8 rounded-full", alt: "User avatar" %>
becomes this :
<%= image_tag "default_avatar.png", data:{src:url_for(@user.avatar)}, class:"w-8 h-8 rounded-full lazy", alt: "User avatar" %>