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: