Create and register Stimulus controllers for interactive JavaScript features. Use when adding client-side interactivity, dynamic UI updates, or when the user mentions Stimulus controllers or JavaScript behavior.
83
79%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Optimize this skill with Tessl
npx tessl skill review --optimize ./skills/stimulus-controllers/SKILL.mdStimulus controllers provide modular JavaScript functionality connected to HTML via data attributes. After creating a new controller, you must register it in the index.js file.
Create a new controller in app/javascript/controllers/:
// app/javascript/controllers/example_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["element"]
static values = { name: String }
connect() {
// Called when controller is connected to DOM
}
disconnect() {
// Called when controller is disconnected from DOM
}
// Action methods
handleClick(event) {
event.preventDefault()
// Your logic here
}
}CRITICAL: After creating a new controller, run:
bin/rails stimulus:manifest:updateThis automatically updates app/javascript/controllers/index.js to register your controller.
Manual Registration (if needed):
import ExampleController from "./example_controller"
application.register("example", ExampleController)Controller name in HTML uses kebab-case: data-controller="example"
Connect the controller to HTML elements:
.container data-controller="example" data-example-name-value="test"
button data-action="click->example#handleClick" Click Me
div data-example-target="element" Target Elementexample_controller.js (snake_case)export default class extends Controller"example" (kebab-case)data-controller="example" (kebab-case)bulk_submit_controller.js → "bulk-submit"Reference specific DOM elements:
static targets = ["input", "output"]
// Access in methods:
this.inputTarget // First matching element
this.inputTargets // All matching elements
this.hasInputTarget // Boolean checkType-safe data attributes:
static values = {
url: String,
count: Number,
active: Boolean,
items: Array,
config: Object
}
// Access in methods:
this.urlValue
this.countValue
// Watch for changes:
urlValueChanged(newUrl, oldUrl) {
// Called when value changes
}Connect events to methods:
<!-- Basic action -->
data-action="click->example#save"
<!-- Multiple actions -->
data-action="click->example#save submit->example#submit"
<!-- Custom events -->
data-action="example:refresh->example#reload"
<!-- Event modifiers -->
data-action="submit->example#save:prevent"Manage CSS classes:
static classes = ["active", "hidden"]
// Use in methods:
this.element.classList.add(this.activeClass)
this.element.classList.remove(this.hiddenClass)export default class extends Controller {
static targets = ["form", "submit"]
validate() {
const isValid = this.formTarget.checkValidity()
this.submitTarget.disabled = !isValid
}
}export default class extends Controller {
static targets = ["content"]
static classes = ["hidden"]
toggle() {
this.contentTarget.classList.toggle(this.hiddenClass)
}
}export default class extends Controller {
static values = { url: String }
async refresh() {
const response = await fetch(this.urlValue)
const html = await response.text()
this.element.innerHTML = html
}
}Test Stimulus controllers in system specs:
it 'handles interaction', :js do
visit page_path
click_button 'Toggle'
expect(page).to have_css('[data-controller="example"]')
endController not working?
index.jsbin/rails stimulus:manifest:update:js tag)Targets not found?
static targets matches HTMLhasXxxTarget to verify existence before accessing097ad6b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.