The team over at Basecamp has released a new Javascript framework (v1.0.0) called Stimulus, available as of January 30th. Stimulus is quite a departure from some of the more popular frontend JavaScript frameworks, touting more separation of concerns between your HTML’s content and behavior:
Stimulus is a JavaScript framework with modest ambitions. Unlike other frameworks, Stimulus doesn’t take over your application’s entire front-end. Rather, it’s designed to augment your HTML by connecting elements to JavaScript objects automatically.
The documentation explains that the separation between your HTML’s behavior and content is bridged by data-controller‘s from HTML to JavaScript.
Here’s a simple example, which I believe is pretty straightforward:
<div data-controller="hello"> <input type="text"> <button data-action="click->hello#greet">Greet</button> </div>
Here’s the hello controller code:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
console.log("Hello, Stimulus!", this.element)
}
}
The data-action="click->hello#greet" button attribute is the ticket here; you can break down the value as follows:
clickis the event namehellois the controller identifiergreetis the name of the method to invoke
We can mark important elements as a target to reference them in the controller:
<div data-controller="hello"> <input data-target="hello.name" type="text"> <button data-action="click->hello#greet">Greet</button> </div>
The data-target value is the controller name and the target name. Here’s how you would reference this value in a controller:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "name" ]
greet() {
const element = this.nameTarget
const name = element.value
console.log(`Hello, ${name}!`)
}
}
Since this is a JavaScript class, we can benefit from a name getter:
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "name" ]
greet() {
console.log(`Hello, ${this.name}!`)
}
get name() {
return this.nameTarget.value
}
}
These samples are from the Hello Stimulus introduction. Stimulus also works with Turbolinks out-of-the-box, which is useful for speeding traditional server-side applications.
You should check out the installation guide and the stimulus starter to get started with the framework.
Using Stimulus With Laravel
I also started experimenting with Stimulus, Turbolinks, and Laravel in a quick GitHub repository paulredmond/stimulus-laravel-demo. Here’s the gist of everything I needed to do to get it running:
yarn add stimulus turbolinks yarn add --dev babel-plugin-transform-class-properties
Add the following to your .babelrc file in the root of the project:
{
"plugins": ["transform-class-properties"],
"presets": [
["env", { "modules": false }]
]
}
Next, create a resources/assets/js/controllers folder and replace the following in the resources/assets/js/app.js file:
var Turbolinks = require("turbolinks")
Turbolinks.start()
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))
One last important note:
Turbolinks needs to be in the <head> of the HTML document. The <body> gets replaced when you navigate after getting the document via XHR, thus, the script needs to be outside of the <body> tag.
So just add your app.js file in the head like this:
<head>
<!-- ... -->
<link href="{{ mix('css/app.css') }}" rel="stylesheet" type="text/css" />
<script src="{{ mix('js/app.js') }}"></script>
</head>
Cheers!