ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

A quick look on Hotwire Stimulus integrated in Rails

By Harvey Ico, server-side engineer from Unifa

So as I was installing new rails app, I noticed that the default javascript library installed now was Stimulus from Hotwire. I find it really good since your javascript now runs only when the data-controller is defined on your page.

This is specially helpful since Rails just includes all your javascript on application.js defined in your application.html.erb which can lead unwanted javascript code running. Up until now, I have been using alternative like unmount to run my javascript when specific query selector has been met.

Installing Stimulus with Yarn

If you're using Rails 7+ with import maps, you may already have Stimulus out of the box. But if you’re using Webpack, esbuild, or Vite, you’ll want to install it via Yarn:

yarn add @hotwired/stimulus

Then, in your JavaScript entrypoint app/javascript/controllers/application.js, set up Stimulus:

import { Application } from "@hotwired/stimulus"


const application = Application.start()
const context = require.context("controllers", true, /\.js$/)
application.load(definitionsFromContext(context))

Then, create app/javascript/controllers/index.js:

import { application } from "./application"

import PhotosController from "./photos_controller"
application.register("photos", PhotosController)

Lastly, app/javascript/application.js

import 'bootstrap';

import "./controllers"

// Rails libraries
import Rails from '@rails/ujs';
Rails.start()

This assumes your controllers live under app/javascript/ folder. @rails/ujs is optional.

Sample usage

You might have noticed, there are PhotosController and bootstrap written on the previous code.

That was because, in my implementation, I was trying to create a javascript interaction whereas; if you click an image, it will open my bootstrap modal and replace the existing value of the said image. Preview Image for short ;)

So my app/javascript/controllers/photos_controller.js look like this:

import { Controller } from "@hotwired/stimulus"
import Modal from 'bootstrap/js/dist/modal';

// Connects to data-controller="photos"
export default class extends Controller {
  static targets = ['modal', 'output']

  connect() {
    this.modal = new Modal(this.modalTarget)
  }

  closePreviewImage(e) {
    e.preventDefault()
    this.modal.hide()
  }

  openPreviewImage(e) {
    e.preventDefault()
    this.outputTarget.src = e.params.src
    this.modal.show()
  }
}

And a sample html would look like this:

<div data-controller="photos">
  <!-- Trigger button -->
  <a data-action="click->photos#openPreviewImage" data-photos-src-param="https://example.com/image.jpg">
    <img src="https://example.com/image.jpg" />
  </a>

  <!-- Modal -->
  <div class="modal fade" tabindex="-1" role="dialog" data-photos-target="modal">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-body">
          <img data-photos-target="output" src="" class="img-fluid" alt="Preview" />
        </div>

        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-action="click->photos#closePreviewImage">
            Close
          </button>
        </div>
      </div>
    </div>
  </div>
</div>

You will notice that stimulus heavily relies on data attributes of html. Most of these data-attributes are predetermined based on what you defined on your controller.

First and foremost, you'll have to set data-controller="photos". This will tell that your page needs to connect to your stimulus PhotosController.

Also in the above example, since I had modal & output target in my stimulus controller, I needed to define data-photos-target="output" and so on. Then for triggering functions inside your stimulus controller, you'll need to use data-action attribute

Tips

"Controller names" in stimulus doesn't necessarily need to be the same controller name as your rails controller. This is just your own unique naming for what your feature is called. In my case, I have both PhotosController on rails controller and stimulus since I only needed to implement it on this page. But lets say, I want to re-use this stimulus controller on other pages as well, I might rename my stimulus controller into PreviewPhotoController instead.

There are various javascript event for data-action depending on the element. Please check the official website for more information: https://stimulus.hotwired.dev/reference/actions

Overall

This kind of library makes your life easier since they only run when the DOM element is present, with some nice additions on data-action, etc. TBH, my experience with this library so far has been smooth. So going forward, I will probably use it on my new projects as well.


Unifa is actively recruiting, please check our website for details:

unifa-e.com