An Introduction To Stimulus.js
The web moves pretty fast and picking an approach for your frontend that will feel sensible in a year’s time is tricky. My biggest fear as a developer is picking up a project that hasn’t been touched for a while, and finding the documentation for whatever approach they used is either non-existent or is not easily found online.
Stimulus has allowed me to build applications in a way that feels reusable and approachable. While I don’t think Stimulus will take over the web like React and Vue have, I think it is a worthwhile tool to learn.
Like all frameworks, Stimulus has preferred terms for describing certain things. Luckily (and one of the main reasons I’ve taken to liking Stimulus), there are only two you’ll need to know about:
This is the name we’ll use to reference our controller in our HTML using a data attribute that is common to Stimulus codebases.
Let’s Jump Into Stimulus!
In the following few examples, I’m going to use code you can drop into the browser to get started right away via the library distributed via unpkg.com. Later on, I’ll cover the webpack approach which is highly encouraged as it allows for improved organization of your code and is the approach used in the Stimulus Handbook.
Once you understand the gist of the above snippet, you’ll have the knowledge to be comfortable picking up a project that uses Stimulus.
Pretty awesome, right? Let’s jump into what everything is doing!
This line tells Stimulus to add the listeners to the page. If you call it just once at the top of your page before you add any Stimulus code, it’ll return an instance of the main Stimulus controller which includes the method
register that is used to tell Stimulus about the classes you’d like to connect to it.
div element. We told Stimulus about the connection between this identifier and the controller via the
Stimulus will continuously monitor your page for when elements with this attribute are added and removed. When a new piece of HTML is added to the page with a
data-controller attribute, it’ll initialize a new instance of the relevant controller class, then connect the HTML element. If you remove that element from the page, it’ll call the
disconnect method on the controller class.
Stimulus uses a data attribute
data-action to clearly define which function of the controller will be run. Using the syntax
event->controller#function anyone reading the HTML will be able to see what it does. This is especially useful as it reduces the risk of unexpected code from other files, making it easier to navigate the codebase. When I first saw the approach Stimulus encourages, it felt almost like reading pseudocode.
In the above example, when the button fires the “click” event, it will be passed onto the
addOne function within our “counter” controller.
Targets are a way to explicitly define which elements are going to be available to your controller. Historically I’ve used a mix of ID, CSS class names and data attributes to achieve this, so having a single “This is the way to do it” which is so explicit makes the code a lot more consistent.
This requires defining your target names within your controller class via the
targets function and adding the name to an element via the
Once you’ve got those two pieces setup, your element will be available in your controller. In this case, I’ve set up the target with the name “output” and it can be accessed by
this.outputTarget within the functions in our controller.
If you have multiple targets with the same name, you can access them by using the plural version of the target method, in this case when I call
this.outputTargets, it’ll return an array containing both my divs with the attribute
To listen to
document events (such as resizing, or the user going offline), you’ll need to append “@window” or “@document” to the
event type (e.g.
resize@window->console#logEvent will call the function
logEvent) on the
console controller when the window is resized.
There is a shorthand way to attach common events, where you are able to omit the event type and it has a default action for the element type. However, I strongly discourage using the event shorthand, as it increases the amount of assumptions someone who is less familiar with Stimulus needs to make about your code.
Uses Multiple Controllers In The Same Element
Quite often you may want to break out two pieces of logic into separate classes, but have them appear close to each other within the HTML. Stimulus allows you to connect elements to multiple classes by placing references to both within your HTML.
In the above example, I’ve set up a
basket object which only concerns itself with counting the total number of items in the basket, but also added a
child object that shows the amount of bags per item.
Passing Data To Your Object
Stimulus provides the methods
this.data.set within the controller class, this will allow you to change data attributes which are within the same namespace as the identifier. By this I mean if you want to pass data to your stimulus controller from your HTML, just add an attribute like
data-[identifier]-a-variable to your HTML element.
this.data.set, it will update the value in your HTML so you can see the value change when you inspect the element using your browser development tools.
Using namespaced data attributes is a really nice way to help make it really clear which data attribute is for what piece of code.
Initialize, Connected, Disconnected
As your application grows, you’ll probably need to hook into ‘lifecycle events’ to set defaults, fetch data, or handle real-time communication. Stimulus has three build-in methods which are called throughout the lifecycle of a Stimulus class.
When Stimulus sees an element with a matching
data-controller attribute, it will create a new version of your controller and call the
initialize function. This will often run when you first load the page, but will also be run if you were to append new HTML to your page (e.g. via AJAX) containing a reference to your controller. It will not run when you move an element to a new position within your DOM.
After a class has been initialized, Stimulus will connect it to the HTML element and call the
connect function. It’ll also call
connect if you were to move an element within your DOM. So if you were to take an element, remove it from one element, then append it somewhere else, you’d notice only
connect will be called.
disconnect function will be run when you remove an element from your page, so for example, if you were to replace the body of your HTML, you could tear down any code which might need to be rerun if the element isn’t in the same position. For example, if you had a fancy WYSIWYG editor which adds lots of extra HTML to an element, you could revert it to its original state when
disconnect was called.
In this example, I set up a parent class named
ParentController, which is then extended by a child class named
ChildController. This let me inherit methods from the
ParentController so I didn’t have to duplicate code within my
Using It In Production
I demonstrated some fairly stand-alone examples of how to use Stimulus above, which should give you a taste of what you can achieve. I also thought I should touch on how I use it in production and how it has worked out for me.
If you’re using Webpack, you’re in for a treat! Stimulus was totally made to be used with Webpack. Their documentation even has a lovely starter kit for Webpack. It’ll allow you to break your controllers into separate files and Stimulus will decide on the correct name to use as an identifier.
You don’t have to use webpack if you want to use Stimulus, but it cleans up the experience a bunch. Personally, Stimulus was the library that helped introduce me to Webpack and really feel the value it offered.
I mentioned in the introduction of this article that I enjoyed using Stimulus because it felt organized. This really becomes apparent when you are combining it with Webpack, which enables auto loading and registration of controllers.
Once you’ve set up Stimulus in Webpack, it’ll encourage you to name your files like
[identifier]_controller.js, where the identifier is what you’ll pass into your HTMLs
As your project grows, you may also want to move your Stimulus controllers into subfolders. In a magical way, Stimulus will convert underscores into dashes, and folder forward slashes into two dashes, which will then become your identifier. So for example, the filename
chat/conversation_item_controller.js will have the identifier
One of my favorite quotes is “The Best Code is No Code At All”, I try to apply this approach to all my projects.
I’ve always been a fan of using the first few milliseconds of a user’s attention getting what I have to share with them — in front of them. Then worrying setting up the interaction layer while the user can start processing what they’re seeing.
I’ve really enjoyed working with Stimulus. There isn’t too much to it, so the learning curve is fairly gentle. I feel pretty confident that if I was to pass my code onto someone else they’ll be happy developers. I’d highly recommend giving it a try, even if it’s just out of pure curiosity.
- Stimulus Homepage
They have a fantastic handbook that goes into the concepts I’ve outlined above into a lot more depth.
- Stimulus GitHub Repository
I’ve learned so much about how Stimulus works by exploring their code.
- Stimulus Cheatsheet
The handbook summarized on a single page.
- Stimulus Forum
Seeing other people working with Stimulus has made me really feel like it’s being used in the wild.