Preview an Image Before Upload with Hotwire/Stimulus

Giving your users the option to preview an image, like a profile picture, can be a great UX improvement. It will also give them a preview on how the image will look within your UI. Specifically useful for the ever fully-rounded profile picture (where it could just look a bit off).

It works like this:

  • give a user the option to upload a profile picture;
  • after selecting an image (via a input type=file);
  • a preview of the image is shown.

Modern JavaScript comes with the FileReader interface that makes this easy. Let’s wrap it in a small Stimulus controller, ready to be copied & pasted in your Rails app.

Let’s look at the required HTML needed first:

<div data-controller="image-preview">
  <img data-image-preview-target="canvas" hidden class="object-cover size-48">

  <input type="file" accept="image/*" data-image-preview-target="source" data-action="image-preview#show">
</div>

Nothing special here. Instantiate the image-preview controller and set the targets: canvas (where the image will be previewed; hidden by default) and a source target. Notice that this input-element also uses a accept attribute that only allows image-types.

And then the full Stimulus controller.

// app/javascripts/controllers/image_preview_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["canvas", "source"];

  show() {
    const reader = new FileReader();

    reader.onload = function () {
      this.canvasTarget.removeAttribute("hidden");

      this.canvasTarget.src = reader.result;
    }.bind(this)

    reader.readAsDataURL(this.sourceTarget.files[0]);
  }
}

The show() function sets up a FileReader. When the image is loaded, the onload function is triggered, which updates the canvas target by setting its src attribute to the loaded file’s result and removing the hidden attribute. Then the file is read, converting it to a Data URL (which can be used directly in img-tags or other elements that accept source attributes).

And that’s it really! 🤯 From here you can make some UI and UX improvements:

  • beautify the input-element with CSS;
  • show a default/fallback profile picture;
  • validate there’s only one image loaded;
  • add an option to remove the profile picture (really straight-forward to do with TurboStream responses!).

Or you can try the Image Preview Component from Rails Designer (feel free to steal some CSS ideas 😅).

Check out this article if you need a way to drag and drop images with this preview image controller.

Need an alternative to S3 to store your images?

Get UI & Product Engineering Insights for Rails Apps (and product updates!)

Published at . Last updated at . Have suggestions or improvements on this content? Do reach out. Interested in sharing Rails Designer with the Ruby on Rails community? Become an affiliate.

UI components for Ruby on Rails apps

$ 99 one-time
payment

Get Access
  • One-time Payment

  • Access to the Entire Library

  • Built for Ruby on Rails

  • Designed with Tailwind CSS and Enhanced with Hotwire

  • Updates for 12 months