As its creators say, Stimulus is a JavaScript framework with modest ambitions, which doesn’t seek to overtake your entire frontend but to augment HTML with just enough behavior to make it shine.
I know what you’re thinking… Another JavaScript framework? There are already so many of them! You’re right, but this one caught my attention as it was created by Basecamp. You probably know this company, they are the creators of Ruby on Rails. They also created Turbolinks, and I mention this because Stimulus pairs beautifully with it. So, maybe it’s worth giving Stimulus a try?
How does it work?
Stimulus works by monitoring the page, waiting for the data-controller
attribute to appear. The data-controller
value connects and disconnects Stimulus controllers. The premise is simple: just as class is a connection between HTML and CSS, data-controller
is a bridge from HTML to JavaScript. Stimulus also adds the data-action
attribute, which describes how events on the page should trigger the controller methods, and the data-target
attribute, which gives you a handle for finding elements in the controller’s scope.
Easy installation
If your Rails app is using a webpacker gem, you can install Stimulus with one simple command as the webpacker gem provides a task for installing Stimulus. Just run the following command in your Rails root directory:
rails webpacker:install:stimulus
This adds Stimulus to package.json
and creates the following two files: app/javascript/controllers/index.js:
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"
const application = Application.start()
const context = require.context("controllers", true, /_controller.js$/)
application.load(definitionsFromContext(context))
You can see that the webpack’s require.context helper is called. It then passed the resulting context to the Application#load method. This means that our application is looking for files named[identifier]_controller.js
in the app/javascript/controllers/
directory, where the identifier corresponds with the controller’s data-controller identifier in your HTML. This is exactly the place where we should put the Stimulus controllers.
The app/javascript/controllers/hello_controller.js
is also generated. This is just an example controller that we can play with.
Basic usage
Ok, so we have the hello_controller.js
file created with the following code:
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ["output"]
connect() {
this.outputTarget.textContent = 'Hello, Stimulus!'
}
}
But, as you know, we won’t see any effects if we don’t make the connection between HTML and JavaScript. Let’s add some code to your HTML view. You can put this whenever you want:
<div data-controller="hello">
<p data-target="hello.output"> </p>
</div>
And you know what? That’s all! If you reload your page and see “Hello, Stimulus!” on your page, this means that the connection is working properly. Simple, right?
Ok, but this is just a plain text display. Can we have some action? Yes, we can! As I mentioned before, there is also the data-action
attribute. Let’s then change our HTML view:
<div data-controller="hello">
<p><input type="number" data-target="hello.days"><br><button data-action="click->hello#calculate">Calculate</button></p>
</div>
And the Stimulus controller app/javascript/controllers/hello_controller.js
:
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ["output", "days"]
connect() {
this.outputTarget.textContent = 'How long have you been in quarantine?'
}
calculate() {
const element = this.daysTarget
const days = element.value
const lockdownDays = 14
let daysLeft = lockdownDays - days
if (daysLeft < 1) {
alert('You are free!')
}
else {
alert(`Amount of days you need to stay on quarantine: ${daysLeft}`)
}
}
}
Here we have a very simple app that calculates how long we must stay in quarantine. We managed to do this by using the data-action attribute in HTML.
You can see that we also added “days” to the list of targets in the JavaScript code. When we do this, Stimulus automatically creates a this.daysTarget
property which returns the first matching target element.
What else do you need to know?
Callbacks
You probably noticed that there is a connect()
method in both examples. This one is for built-in callbacks. You should know all of them and their lifecycle, so here they are:
initialize
: once, when the controller is first instantiated,
connect
: anytime the controller is connected to the DOM,
disconnect
: anytime the controller is disconnected from the DOM.
Action descriptor
The data-action
value click->hello#calculate
is called an action descriptor. This descriptor says that:
click
is the event name,
hello
is the controller identifier,
calculate
is the name of the method.
Targets
Just like for actions, Stimulus has a target descriptor. The hello.days
descriptor says that: hello
is the controller identifier,
days
is the target name.
In the previous example, I mentioned that Stimulus creates the this.daysTarget
property when there is “days” in the list of targets. You also need to know that more properties can be created. In relation to the example, you can have:
this.daysTarget
which evaluates up to the first target in the controller’s scope. If there is no target, the property flags an error,
this.daysTargets
evaluates up to an array of all source targets in the controller’s scope,
this.hasDaysTarget
returns true if there is a target or false if not.
As you can see, Stimulus is simple and can easily be added to your application. It’s gaining some popularity in the RoR community and I’m not surprised. As a Ruby developer myself, I really enjoy working with Stimulus. So how about taking it for a spin?
Read more: