Loading...
Loading...
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.
npx skill4agent add smnandre/symfony-ux-skills stimulusdata-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// 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
}
}hello_controller.jsdata-controller="hello"--components/modal_controller.jsdata-controller="components--modal"<div data-controller="hello">
<input data-hello-target="name" type="text">
<button data-action="click->hello#greet">Greet</button>
<span data-hello-target="output"></span>
</div><div data-controller="map"
data-map-latitude-value="{{ place.lat }}"
data-map-longitude-value="{{ place.lng }}"
data-map-zoom-value="12">
</div>StringNumberBooleanArrayObject{name}ValueChanged()event->controller#method{# Explicit event #}
<button data-action="click->hello#greet">Greet</button>
{# Default event (click for button) #}
<button data-action="hello#greet">Greet</button>
{# Multiple actions on same element #}
<input type="text"
data-action="focus->field#highlight blur->field#normalize input->field#validate">
{# Prevent default #}
<form data-action="submit->form#validate:prevent">
{# Keyboard shortcuts #}
<div data-action="keydown.esc@window->modal#close">
<input data-action="keydown.enter->modal#submit keydown.ctrl+s->modal#save">
{# Global events (window/document) #}
<div data-action="resize@window->sidebar#adjust click@document->sidebar#closeOutside"><button data-controller="button"
data-button-loading-class="opacity-50 cursor-wait"
data-button-active-class="bg-blue-600"
data-action="click->button#submit">
Submit
</button>// In controller
this.element.classList.add(...this.loadingClasses);<div data-controller="dropdown tooltip"
data-action="mouseenter->tooltip#show mouseleave->tooltip#hide">
<button data-action="click->dropdown#toggle">Menu</button>
<ul data-dropdown-target="menu" hidden>...</ul>
</div><div data-controller="player"
data-player-playlist-outlet="#playlist">
<button data-action="click->player#playNext">Next</button>
</div>
<ul id="playlist" data-controller="playlist">
<li data-playlist-target="track">Song 1</li>
<li data-playlist-target="track">Song 2</li>
</ul>// In player controller
static outlets = ['playlist'];
playNext() {
const tracks = this.playlistOutlet.trackTargets;
// ...
}/* 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
}
}/* stimulusFetch: 'lazy' */{# Raw attributes (preferred) #}
<div data-controller="search"
data-search-url-value="{{ path('api_search') }}">{# Twig helper #}
<div {{ stimulus_controller('search', { url: path('api_search') }) }}>
{# Chaining multiple controllers #}
<div {{ stimulus_controller('a')|stimulus_controller('b') }}>
{# Target and action helpers #}
<input {{ stimulus_target('search', 'query') }}>
<button {{ stimulus_action('search', 'submit') }}>connect()disconnect()data-*