--- description: globs: alwaysApply: false --- This rule describes how to write Stimulus controllers. - **Use declarative actions, not imperative event listeners** - Instead of assigning a Stimulus target and binding it to an event listener in the initializer, always write Controllers + ERB views declaratively by using Stimulus actions in ERB to call methods in the Stimulus JS controller. Below are good vs. bad code. BAD code: ```js // BAD!!!! DO NOT DO THIS!! // Imperative - controller does all the work export default class extends Controller { static targets = ["button", "content"] connect() { this.buttonTarget.addEventListener("click", this.toggle.bind(this)) } toggle() { this.contentTarget.classList.toggle("hidden") this.buttonTarget.textContent = this.contentTarget.classList.contains("hidden") ? "Show" : "Hide" } } ``` GOOD code: ```erb
``` ```js // Declarative - controller just responds export default class extends Controller { static targets = ["button", "content"] toggle() { this.contentTarget.classList.toggle("hidden") this.buttonTarget.textContent = this.contentTarget.classList.contains("hidden") ? "Show" : "Hide" } } ``` - **Keep Stimulus controllers lightweight and simple** - Always aim for less than 7 controller targets. Any more is a sign of too much complexity. - Use private methods and expose a clear public API - **Keep Stimulus controllers focused on what they do best** - Domain logic does NOT belong in a Stimulus controller - Stimulus controllers should aim for a single responsibility, or a group of highly related responsibilities - Make good use of Stimulus's callbacks, actions, targets, values, and classes - **Component controllers should not be used outside the component** - If a Stimulus controller is in the app/components directory, it should only be used in its component view. It should not be used anywhere in app/views.