--- id: smnandre/symfony-ux-skills/stimulus version: "77785dec" license: MIT install: manual updated: 2026-06-14 --- # stimulus — Stimulus is a lightweight JavaScript framework that wires controllers to HTML via data attributes, letting you handle clicks, inputs, and DOM changes without rendering markup. Attach behavior to server-rendered elements using `data-controller`, `data-action`, and `data-target` attributes, then compose controllers for toggles, modals, dropdowns, and more. Clean up in `disconnect()` when Turbo navigates between pages. Publisher: smnandre · Stars: 164 · Updated: 2026-06-14 Install (manual): `git clone https://github.com/smnandre/symfony-ux-skills` ## SKILL.md # 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 #}