Stimulus: A JavaScript Framework for People Who Love HTML

Stimulus is a “modest JavaScript framework”, built by the folks at Basecamp who brought you Rails.

Stimulus: A JavaScript Framework for People Who Love HTML

It is, in many ways, the opposite of other modern frameworks available today. While you might see some similar concepts and naming, Stimulus is very different in some fundamental ways. We’ll discuss those differences briefly, and then build our first application with Stimulus.

Stimulus: A JavaScript Framework for People Who Love HTML

The DOM: A Place for State?

The core concepts of many modern frameworks lie in separating “state” from the “DOM”. In this way, the DOM acts only as a client to the data it is representing.

Stimulus flips this concept on its head, and instead relies on the DOM to hold state.

What this ultimately means is that Stimulus is best suited for applications that serve HTML (rather than, for example, integrating with a JSON API).

With Stimulus, you won’t see HTML templates. Instead, you will see data-* attributes that connect the HTML to the JavaScript application.

This also means you won’t see things like each or map loops like you might see in Handlebars or React.

Stimulus doesn’t hold the job of rendering HTML unless you explicitly create that functionality.

So… What Does It Do?

So Stimulus doesn’t render templates. Rather it is built to connect actions and events you take on the front end of an application to controllers on the back end.

Stimulus uses three concepts to accomplish this: targets, controllers, and actions.

But before we get too far in, let’s go ahead and get Stimulus up and running on your computer!


This setup assumes you have already installed a recent version of NodeJS on your computer. Head over to if you haven’t done this already.

We’ll be using a basic static index.html file that will initially look like this:

<!doctype html>
    <link rel="stylesheet" href="./dist/bundle.css">
    <!-- application code will go here -->
    <script src="./dist/bundle.js"></script>

Note: We won’t actually cover any styling or build processes for the CSS in this tutorial.

Next, create a folder called src. Inside of src, create another folder called controllers and a single index.js file.

We’ll be using Webpack to build our JavaScript application, since Stimulus uses some advanced features of JavaScript that won’t work directly in browsers.

Create a file in the root of your project called package.json that has the following contents:

    "name": "wdstimulus",
    "version": "1.0.0",
    "description": "Stimulus Introduction",
    "scripts": {},
    "author": "Your Name",
    "license": "ISC",
    "devDependencies": {
        "@babel/core": "^7.0.0-beta.39",
        "@babel/preset-env": "^7.0.0-beta.39",
        "babel-loader": "^8.0.0-beta.0",
        "babel-preset-stage-0": "^6.24.1",
        "stimulus": "^1.0.0",
        "webpack": "^3.10.0"

Note: these versions will change in the future, and we recommend staying up to date with the latest version of any tool as much as is practical.

You can also generate this file using npm init and installing dependencies one by one using npm install [package-name].

This file includes everything we need to create a browser-compatible build of our Stimulus application. From the root of the application, run npm install. This will install these dependencies in the node_modules directory inside your application.

Next, we’ll create a configuration file so Webpack knows what we want it to do with our files when we save them. In the same root directory where the package.json is located, created a file called webpack.config.js, and add these contents:

module.exports = {
    module: {
        rules: [
                test: /.js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/env"],
                        plugins: ["transform-class-properties"]

This file essentially tells Webpack to compile our JavaScript using the env preset. We have the stage-0 preset available in our node modules, and we’ve also added the required transform-class-properties plugin.

The last step to be ready for a Stimulus application is to fill in our index.js file with the following contents:

import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

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

This code comes directly from the Stimulus docs. It performs some magic to allow us to use a conventional naming structure when creating our application code.

The naming of your files actually matters to Stimulus if you use this index.js code (this nuance is something Rails developers will find familiar).

Now that we’re all set up, let’s make sure our Webpack build works. From the root directory, run the following command:

npx webpack src/index.js dist/bundle.js --watch

The npx portion of this command is a convenient way of running binaries that are located in the node_modules directory. This command will watch changes to your JavaScript files and rebuild your application as you make those changes.

As long as you don’t see errors in your console, you are good to go!


If you’re familiar with Rails or Laravel, the concept of controllers might be easy to adopt for you.

Think of a controller in the case of Stimulus as a JavaScript class that contains some methods and data that work together.

A controller might look like this in Stimulus:

<div data-controller="refreshable">
<!-- more soon... -->
// src/controllers/refreshable_controller.js
import { Controller } from "stimulus"

export default class extends Controller {

There are a few important points to note about this controller. First, the controller is both HTML and JavaScript. The data-controller attribute is what tells the JavaScript to create a new instance of the class.

Another interesting characteristic to notice is the fact that the class name isn’t found anywhere in the controller class itself. This happens because of the line application.load(definitionsFromContext(context)) in the index.js file. This line loads the application code and uses the file names to infer class names. This might be confusing at first, but once you start using this convention regularly, it becomes much more fluid.


An action is what it sounds like: a procedure you want to take place after a particular event from a user. For our refresh controller, we want to perform a refresh of a particular area of content when a button is clicked. In this case, our action could be called refresh.

// src/controllers/refreshable_controller.js
import { Controller } from "stimulus"

export default class extends Controller {

But just having this action method available in our controller doesn’t do anything. We need to connect it to our HTML.

<div data-controller="refreshable">
  <button data-action="refreshable#refresh">Refresh</button>

When the user clicks on the button in this HTML, it would run the refresh action in the controller.

Now, how can we use this action to do something on the page?


Stimulus also uses a concept called “Targets” to connect to important elements on the page. We’ll use this to manage a container element that holds the content we want to refresh.

<div data-controller="refreshable">
  <button data-action="refreshable#refresh">Refresh</button>
  <article data-target="refreshable.content"></article>
// src/controllers/refreshable_controller.js
import { Controller } from "stimulus"

export default class extends Controller {

  static targets = [ "content" ]

    this.contentTarget.innerHTML = "Refresh!"


In this example, we have all three concepts of Stimulus represented. A refreshable controller, a refresh action, and a content target.

If you’re looking closely, you’ll see something in this code that might catch you off guard: direct manipulation of the DOM. This is one way that Stimulus is different from other frameworks. Direct manipulation of DOM elements isn’t only possible, it is encouraged.

This also means that if you are well versed in jQuery or vanilla JavaScript and browser APIs, you can take advantage of that knowledge! For example, a jQuery-powered version of the refresh method might look like this:

// src/controllers/refreshable_controller.js
import { Controller } from "stimulus"
import $ from 'jquery'; // requires adding jQuery to package.json

export default class extends Controller {

  static targets = [ "content" ]

    let target = this.contentTarget;
    $.get("/content", function(content){


Stimulus doesn’t get in the way if you want to use other tools you are already familiar with.

That’s All for This Crash Course!

You now have enough info to get started building more maintainable applications with Stimulus! The developers who created Stimulus remind us that rules and trends can be broken, and sometimes we can break away from the “best practices” and just do something that works for us.

Enjoy diving into Stimulus!

Further Reading