Speed up your UX by deferring image loading with placeholders

JS Ruby on Rails Stimulus

Deferring the loading of your images with placeholders can prove really helpful for your user experience and application performance. Here's a simple implementation that can help you achieve that in Rails!

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.

  1. 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
  2. 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 in application.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();
  3. 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" %>

→ Let's get in touch

Got questions or feedback about this article? Interested in discussing your project? I'm all ears and always open to new opportunities. Shoot me an email and tell me what you have in mind.