The best way to learn how Stimulus works is to build a simple controller. This chapter will show you how.
To follow along, you'll need a running copy of the stimulus-starter
project, which is a preconfigured blank slate for exploring Stimulus.
We recommend remixing stimulus-starter
on Glitch so you can work entirely in your browser without installing anything:
Or, if you'd prefer to work from the comfort of your own text editor, you'll need to clone and set up stimulus-starter
:
$ git clone https://github.com/stimulusjs/stimulus-starter.git
$ cd stimulus-starter
$ yarn install
$ yarn start
Then visit http://localhost:9000/ in your browser.
(Note that the stimulus-starter
project uses the Yarn package manager for dependency management, so make sure you have that installed first.)
Let's begin with a simple exercise: a text field with a button. When you click the button, we'll display the value of the text field in the console.
Every Stimulus project starts with HTML, and this project is no exception. Open public/index.html
and add the following markup just after the opening <body>
tag:
<div>
<input type="text">
<button>Greet</button>
</div>
Reload the page in your browser and you should see the text field and button.
At its core, Stimulus' purpose is to automatically connect DOM elements to JavaScript objects. Those objects are called controllers.
Let's create our first controller by extending the framework's built-in Controller
class. Create a new file named hello_controller.js
in the src/controllers/
folder. Then place the following code inside:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
}
Next, we need to tell Stimulus how this controller should be connected to our HTML. We do this by placing an identifier in the data-controller
attribute on our <div>
:
<div data-controller="hello">
<input type="text">
<button>Greet</button>
</div>
Identifiers serve as the link between elements and controllers. In this case, the identifier hello
tells Stimulus to create an instance of the controller class in hello_controller.js
. Learn how automatic controller loading works in the Installation Guide.
Reload the page in your browser and you'll see that nothing has changed. How do we know whether our controller is working or not?
One way is to put a log statement in the connect()
method, which Stimulus calls each time a controller is connected to the document.
Implement the connect()
method in hello_controller.js
as follows:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
console.log("Hello, Stimulus!", this.element)
}
}
Reload the page again and open the developer console. You should see Hello, Stimulus!
followed by a representation of our <div>
.
Now let's see how to change the code so our log message appears when we click the "Greet" button instead.
Start by renaming connect()
to greet()
:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
console.log("Hello, Stimulus!", this.element)
}
}
We want to call the greet()
method when the button's click
event is triggered. In Stimulus, controller methods which handle events are called action methods.
To connect our action method to the button's click
event, open public/index.html
and add a magic data-action
attribute to the button:
<div data-controller="hello">
<input type="text">
<button data-action="click->hello#greet">Greet</button>
</div>
The
data-action
valueclick->hello#greet
is called an action descriptor. This particular descriptor says:
click
is the event namehello
is the controller identifiergreet
is the name of the method to invoke
Load the page in your browser and open the developer console. You should see the log message appear when you click the "Greet" button.
We'll finish the exercise by changing our action to say hello to whatever name we've typed in the text field.
In order to do that, first we need a reference to the input element inside our controller. Then we can read the value
property to get its contents.
Stimulus lets us mark important elements as targets so we can easily reference them by name. Open public/index.html
and add a magic data-target
attribute to the input element:
<div data-controller="hello">
<input data-target="hello.name" type="text">
<button data-action="click->hello#greet">Greet</button>
</div>
The
data-target
valuehello.name
is called a target descriptor. This particular descriptor says:
hello
is the controller identifiername
is the target name
If we pass the target name to the this.targets.find()
method, Stimulus will return the first matching target element it finds. We can then read its value
and use it to build our greeting string.
Let's try it out. Open hello_controller.js
and update the greet()
method like so:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
const element = this.targets.find("name")
const name = element.value
console.log(`Hello, ${name}!`)
}
}
Then reload the page in your browser and open the developer console. Enter your name in the input field and click the "Greet" button. Hello, world!
We've seen that Stimulus controllers are instances of JavaScript classes whose methods can act as event handlers.
That means we have an arsenal of standard refactoring techniques at our disposal. For example, we can clean up our greet()
method by extracting a name
getter:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
console.log(`Hello, ${this.name}!`)
}
get name() {
const target = this.targets.find("name")
return target.value
}
}
And we can further clean up the name
getter by extracting an inputElement
getter for the target element:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
console.log(`Hello, ${this.name}!`)
}
get name() {
return this.inputElement.value
}
get inputElement() {
return this.targets.find("name")
}
}
Now the most important code lives at the top of our controller and its supporting details live below.
Congratulations—you've just written your first Stimulus controller!
We've covered the framework's core concepts: controllers, identifiers, actions, and targets. In the next chapter, we'll see how to put those together to build a real-life controller taken right from Basecamp.
Next: Building Something Real