--- name: stimulus description: Stimulus JS framework for Symfony UX. Use when building client-side interactivity with data attributes, creating controllers for DOM manipulation, handling user events, managing component state, or integrating with Symfony's StimulusBundle and AssetMapper. Triggers - stimulus controller, data-controller, data-action, data-target, frontend interactivity, JavaScript behavior, Symfony UX frontend, toggle, dropdown, modal JS, tabs JS, clipboard, chart controller, datepicker, autocomplete JS, lazy controller, stimulusFetch, outlets, keyboard shortcut, global event listener. Also trigger when the user wants to add JavaScript behavior to server-rendered HTML, wrap a third-party JS library, or build client-only interactions that don't need a server round-trip. license: MIT metadata: author: Simon Andre email: smn.andre@gmail.com url: https://smnandre.dev version: "1.0" --- # Stimulus Modest JavaScript framework that connects JS objects to HTML via data attributes. Stimulus does not render HTML -- it augments server-rendered HTML with behavior. The mental model: HTML is the source of truth, JavaScript controllers attach to elements, and data attributes are the wiring. No build step required with AssetMapper. ## Quick Reference ``` data-controller="name" attach controller to element data-name-target="item" mark element as a target data-action="event->name#method" bind event to controller method data-name-key-value="..." pass typed data to controller data-name-key-class="..." configure CSS class names data-name-other-outlet=".selector" reference another controller instance ``` ## Controller Skeleton ```javascript // assets/controllers/example_controller.js import { Controller } from '@hotwired/stimulus'; export default class extends Controller { static targets = ['input', 'output']; static values = { url: String, delay: { type: Number, default: 300 } }; static classes = ['loading']; static outlets = ['other']; connect() { // Called when controller connects to DOM } disconnect() { // Called when controller disconnects -- clean up here } submit(event) { // Action method } } ``` File naming convention: `hello_controller.js` maps to `data-controller="hello"`. Subdirectories use `--` as separator: `components/modal_controller.js` maps to `data-controller="components--modal"`. ## HTML Wiring Examples ### Basic Controller ```html
``` ### Values from Server (Twig) Pass server data to controllers via value attributes. Values are typed and automatically parsed. ```html
``` Available types: `String`, `Number`, `Boolean`, `Array`, `Object`. Values trigger `{name}ValueChanged()` callbacks when mutated. ### Actions The format is `event->controller#method`. Default events exist per element type (click for buttons, input for inputs, submit for forms) so the event can be omitted. ```html {# Explicit event #} {# Default event (click for button) #} {# Multiple actions on same element #} {# Prevent default #}
{# Keyboard shortcuts #}
{# Global events (window/document) #}
``` ### CSS Classes Externalize CSS class names so controllers stay generic: ```html ``` ```javascript // In controller this.element.classList.add(...this.loadingClasses); ``` ### Multiple Controllers An element can have multiple controllers: ```html
``` ### Outlets (Cross-Controller Communication) Reference other controller instances by CSS selector: ```html
  • Song 1
  • Song 2
``` ```javascript // In player controller static outlets = ['playlist']; playNext() { const tracks = this.playlistOutlet.trackTargets; // ... } ``` ### Lazy Loading (Heavy Dependencies) Load controller JS only when the element appears in the viewport. Use for controllers with heavy dependencies (chart libs, editors, maps). ```javascript /* stimulusFetch: 'lazy' */ import { Controller } from '@hotwired/stimulus'; import Chart from 'chart.js'; export default class extends Controller { connect() { // Chart.js is only loaded when this element enters the viewport } } ``` The `/* stimulusFetch: 'lazy' */` comment must be the very first line of the file. ## Symfony / Twig Integration Raw data attributes are the recommended approach -- they work everywhere, are easy to read, and need no special helpers. ```twig {# Raw attributes (preferred) #}
``` Twig helpers exist for complex cases or when generating attributes programmatically: ```twig {# Twig helper #}
{# Chaining multiple controllers #}
{# Target and action helpers #}