Installing Stimulus JS in a Rails app

The purpose of this post is to explain how someone would install Stimulus JS in an existing Rails application and show some examples/recommendations on how to organize code within it.

This article is not to about explaining the Stimulus JS framework. If you are beginning with Stimulus, I’d recommend checking out their getting started guide, which is super lightweight and very easy to read.

Integrating Stimulus JS with a Rails application

Using Stimulus inside a Rails app should be fairly easy. Depending on your application stack, the installation can be a little different. It will be mainly a matter of whether your app is using webpacker or not.

If you’re not interested in using webpacker, you can skip the next section and jump right into the Stimulus without webpacker part.

Stimulus with webpacker

If your Rails app is using the webpacker gem, installing Stimulus should be pretty straight forward — just a few commands away.

The webpacker gem provides a task for installing everything necessary to run Stimulus JS. Go to your Rails root directory and run the following command:

bundle exec rails webpacker:install:stimulus

This will add Stimulus to your package.json, just like if you had run yarn add stimulus. Additionally, it will add the following code inside app/javascripts/packs/application.js:

// app/javascripts/packs/application.js
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"
const application = Application.start()
const context = require.context("controllers", true, /.js$/)
application.load(definitionsFromContext(context))

This is the code that will boot the Stimulus application so that it can detect the Stimulus annotations declared in your HTML views.

You also need to make sure that app/javascripts/packs/application.js is being properly loaded. If you don’t have it yet, add the following code to your layout or view:

<%= javascript_pack_tag "application" %>

Just like javascript_include_tag, javascript_pack_tag will load a JS file from app/javascripts/packs/, which typically acts as the entry point for a webpack application.

With that in place, you should be able to start adding some Stimulus magic into your HTML. And if you placed your Stimulus controllers under app/javascripts/controllers/, then everything should just work. Let’s see a hello_controller example.

Example

Add this HTML into a Rails view where your pack application is loaded:

<div data-controller="hello">
<h1 data-target="hello.output"></h1>
</div>

Then add the following Stimulus controller under app/javascripts/controllers/hello_controller.js:

// app/javascripts/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "output" ]
  connect() {
this.outputTarget.textContent = 'Hello, Stimulus!'
}
}

Reload the page. That’s all!

If you see “Hello, Stimulus!” rendered on the page, it means everything is working. You can now start adding more controllers and behavior to your application.

Stimulus without webpacker

Of course, you can use Stimulus JS without webpacker. Stimulus official website states:

If you prefer not to use a build system, you can load Stimulus in a <script> tag and it will be globally available through the window.Stimulus object. [stimulusjs.org]

If you want to do that while still taking advantage of Rails’ asset pipeline, you’ll need to download the Stimulus JS file and install it in your project manually.

Use the following command to install stimulus.umd.js under your vendor/ folder:

wget -O vendor/assets/javascripts/stimulus.umd.js https://unpkg.com/stimulus/dist/stimulus.umd.js

(Here’s a handy link to the Stimulus website where you can make sure we are still using the latest url).

You should now have a new vendor/assets/javascripts/stimulus.umd.js file in your project. This will expose a window.Stimulus object for you to use, which will be useful for starting the Stimulus application.

Now create an app/assets/javascripts/stimulus/init.js file, where you’ll require the stimulus framework, and start it up. Like this:

//= require stimulus.umd
(() => {
if (!("stimulus" in window)) {
window.stimulus = Stimulus.Application.start()
}
})()

We are starting the Stimulus application and attaching the resulting object to the document window. We need to do this since the intention is to share the stimulus object across the different Stimulus controllers. This will allow you to define Stimulus controllers in different JS files.

Now go ahead and require the initialization file in your application manifest, typically at app/assets/javascripts/application.js:

//= require stimulus/init
//= require_tree .

With that in place, you should be able to start creating some Stimulus controllers. Check the hello_controller example below to see it in action.

Example

Add this HTML into a Rails view:

<div data-controller="hello">
<h1 data-target="hello.output"></h1>
</div>

Then the following Stimulus controller under app/assets/javascripts/controllers/hello_controller.js:

// app/assets/javascripts/controllers/hello_controller.js
(() => {
stimulus.register("hello", class extends Stimulus.Controller {
static get targets() {
return [ "output" ]
}
    connect() {
this.outputTarget.textContent = 'Hello, Stimulus!'
}
})
})()

Reload the page. It works!

Notice how the controller code from this example is different from the version using webpacker. That’s because we lost some of the syntactic sugar that comes with Babel (included in webpacker). A few notes on this:

  • We are using the shared stimulus object from window to register the hello_controller.
  • We are using static get targets() method instead of static targets [...] class properties, which aren’t supported natively yet.

You should be able to add as many controllers as you need under app/assets/javascripts/controllers/. Just keep in mind that you’ll need to declare every controller using the shared stimulus object. This is how someone would register a controllers/foo_controller.js:

stimulus.register("foo", class extends Stimulus.Controller {
// ...
}

Conclusion

Adding Stimulus JS to an existing Rails application is easy. Especially if you are already using webpacker. It’s just a matter of running a rake task and you’ll be basically ready to go.

On the other hand, Rails apps not using webpacker need a little more manual setup, like the additional plumbing needed for breaking the Stimulus controllers into different files.

Either way, any of the two options work and seem to be production-ready. So, give it a try! — if you are comfortable with the Stimulus framework, of course.

Personally, I kind of like it.