Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Bundles
  4. StimulusBundle

StimulusBundle: Symfony integration with Stimulus

Edit this page

Tip

Check out live demos of Symfony UX at https://ux.symfony.com!

This bundle adds integration between Symfony, Stimulus and the Symfony UX packages:

  • Twig stimulus_ functions & filters to add Stimulus controllers,
    actions & targets in your templates;
  • Integration to load UX Packages (extra Stimulus controllers)

Installation

First, if you don't have one yet, choose and install an asset handling system; both work great with StimulusBundle:

  • AssetMapper: PHP-based system for handling assets

or

  • Webpack Encore Node-based packaging system

See Encore vs AssetMapper to learn which is best for your project.

Next, install the bundle:

1
$ composer require symfony/stimulus-bundle

If you're using Symfony Flex, you're done! The recipe will update the necessary files. If not, or you're curious, see Manual Setup.

Tip

If you're using Encore, be sure to install your assets (e.g. npm install) and restart Encore.

Usage

You can now create custom Stimulus controllers inside of the assets/controllers directory. In fact, you should have an example controller there already: hello_controller.js:

1
2
3
4
5
6
7
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    connect() {
        this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
    }
}

Then, activate the controller in your HTML:

1
2
3
<div data-controller="hello">
   ...
</div>

Optionally, this bundle has a Twig function to render the attribute:

1
2
3
4
5
6
7
8
<div {{ stimulus_controller('hello') }}>
    ...
</div>

<!-- would render -->
<div data-controller="hello">
   ...
</div>

That's it! Whenever this element appears on the page, the hello controller will activate.

There's a lot more to learn about Stimulus. See the Stimulus Documentation for all the goodies.

TypeScript Controllers

If you want to use TypeScript to define your controllers, you can! Install and set up the sensiolabs/typescript-bundle. Then be sure to add the assets/controllers path to the sensiolabs_typescript.source_dir configuration. Finally, create your controller in that directory and you're good to go.

The UX Packages

Symfony provides a set of UX packages that add extra Stimulus controllers to solve common problems. StimulusBundle activates any 3rd party Stimulus controllers that are mentioned in your assets/controllers.json file. This file is updated whenever you install a UX package.

The official UX packages are:

  • ux-autocomplete: Transform EntityType, ChoiceType or any <select> element into an Ajax-powered autocomplete field (see demo)
  • ux-chartjs: Easy charts with Chart.js (see demo)
  • ux-cropperjs: Form Type and tools for cropping images (see demo)
  • ux-dropzone: Form Type for stylized "drop zone" for file uploads (see demo)
  • ux-lazy-image: Optimize Image Loading with BlurHash (see demo)
  • ux-live-component: Build Dynamic Interfaces with Zero JavaScript (see demo)
  • ux-notify: Send server-sent native notification with Mercure (see demo)
  • ux-react: Render React component from Twig (see demo)
  • ux-svelte: Render Svelte component from Twig (see demo)
  • ux-swup: Integration with Swup (see demo)
  • ux-toggle-password: Toggle visibility of password inputs (see demo)
  • ux-translator: Use your Symfony translations in JavaScript Swup (see demo)
  • ux-turbo: Integration with Turbo Drive for a single-page-app experience (see demo)
  • ux-twig-component: Build Twig Components Backed by a PHP Class (see demo)
  • ux-typed: Integration with Typed (see demo)
  • ux-vue: Render Vue component from Twig (see demo)

Lazy Stimulus Controllers

By default, all of your controllers (i.e. files in assets/controllers/ + controllers in assets/controllers.json) will be downloaded and loaded on every page.

Sometimes you may have a controller that's only used on some pages. In that case, you can make the controller "lazy". In this case, will not be downloaded on initial page load. Instead, as soon as an element appears on the page matching the controller (e.g. <div data-controller="hello">), the controller - and anything else it imports - will be lazily-loaded via Ajax.

To make one of your custom controllers lazy, add a special comment on top:

1
2
3
4
5
6
import { Controller } from '@hotwired/stimulus';

/* stimulusFetch: 'lazy' */
export default class extends Controller {
    // ...
}

To make a third-party controller lazy, in assets/controllers.json, set fetch to lazy.

Note

If you write your controllers using TypeScript, make sure removeComments is not set to true in your TypeScript config.

Stimulus Tools around the World

Because Stimulus is used by developers outside of Symfony, many tools exist beyond the UX packages:

  • stimulus-use: Add composable behaviors to your Stimulus controllers, like debouncing, detecting outside clicks and many other things.
  • stimulus-components A large number of pre-made Stimulus controllers, like for Copying to clipboard, Sortable, Popover (similar to tooltips) and much more.

Stimulus Twig Helpers

This bundle adds some Twig functions/filters to help add Stimulus controllers, actions and targets in your templates.

Note

Though this bundle provides these helpful Twig functions/filters, it's recommended to use raw data attributes instead, as they're straightforward.

Tip

If you use PhpStorm IDE - you may want to install Stimulus plugin to get nice auto-completion for the attributes.

stimulus_controller

This bundle ships with a special stimulus_controller() Twig function that can be used to render Stimulus Controllers & Values and CSS Classes. Stimulus Controllers can also reference other controllers by using Outlets.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
<div {{ stimulus_controller('chart', { 'name': 'Likes', 'data': [1, 2, 3, 4] }) }}>
    Hello
</div>

<!-- would render -->
<div
   data-controller="chart"
   data-chart-name-value="Likes"
   data-chart-data-value="&#x5B;1,2,3,4&#x5D;"
>
   Hello
</div>

If you want to set CSS classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div {{ stimulus_controller('chart', { 'name': 'Likes', 'data': [1, 2, 3, 4] }, { 'loading': 'spinner' }) }}>
    Hello
</div>

<!-- would render -->
<div
   data-controller="chart"
   data-chart-name-value="Likes"
   data-chart-data-value="&#x5B;1,2,3,4&#x5D;"
   data-chart-loading-class="spinner"
>
   Hello
</div>

<!-- or without values -->
<div {{ stimulus_controller('chart', controllerClasses = { 'loading': 'spinner' }) }}>
    Hello
</div>

And with outlets:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div {{ stimulus_controller('chart', { 'name': 'Likes', 'data': [1, 2, 3, 4] }, { 'loading': 'spinner' }, { 'other': '.target' ) }}>
    Hello
</div>

<!-- would render -->
<div
   data-controller="chart"
   data-chart-name-value="Likes"
   data-chart-data-value="&#x5B;1,2,3,4&#x5D;"
   data-chart-loading-class="spinner"
   data-chart-other-outlet=".target"
>
   Hello
</div>

<!-- or without values/classes -->
<div {{ stimulus_controller('chart', controllerOutlets = { 'other': '.target' }) }}>
    Hello
</div>

Any non-scalar values (like data: [1, 2, 3, 4]) are JSON-encoded. And all values are properly escaped (the string &#x5B; is an escaped [ character, so the attribute is really [1,2,3,4]).

If you have multiple controllers on the same element, you can chain them as there's also a stimulus_controller filter:

1
2
3
4
5
6
7
8
<div {{ stimulus_controller('chart', { 'name': 'Likes' })|stimulus_controller('other-controller') }}>
    Hello
</div>

<!-- would render -->
<div data-controller="chart other-controller" data-chart-name-value="Likes">
    Hello
</div>

You can also retrieve the generated attributes as an array, which can be helpful e.g. for forms:

1
{{ form_start(form, { attr: stimulus_controller('chart', { 'name': 'Likes' }).toArray() }) }}

stimulus_action

The stimulus_action() Twig function can be used to render Stimulus Actions.

For example:

1
2
3
4
5
6
<div {{ stimulus_action('controller', 'method') }}>Hello</div>
<div {{ stimulus_action('controller', 'method', 'click') }}>Hello</div>

<!-- would render -->
<div data-action="controller#method">Hello</div>
<div data-action="click->controller#method">Hello</div>

If you have multiple actions and/or methods on the same element, you can chain them as there's also a stimulus_action filter:

1
2
3
4
5
6
7
8
<div {{ stimulus_action('controller', 'method')|stimulus_action('other-controller', 'test') }}>
    Hello
</div>

<!-- would render -->
<div data-action="controller#method other-controller#test">
    Hello
</div>

You can also retrieve the generated attributes as an array, which can be helpful e.g. for forms:

1
{{ form_row(form.password, { attr: stimulus_action('hello-controller', 'checkPasswordStrength').toArray() }) }}

You can also pass parameters to actions:

1
2
3
4
<div {{ stimulus_action('hello-controller', 'method', 'click', { 'count': 3 }) }}>Hello</div>

<!-- would render -->
<div data-action="click->hello-controller#method" data-hello-controller-count-param="3">Hello</div>

stimulus_target

The stimulus_target() Twig function can be used to render Stimulus Targets.

For example:

1
2
3
4
5
6
<div {{ stimulus_target('controller', 'myTarget') }}>Hello</div>
<div {{ stimulus_target('controller', 'myTarget secondTarget') }}>Hello</div>

<!-- would render -->
<div data-controller-target="myTarget">Hello</div>
<div data-controller-target="myTarget secondTarget">Hello</div>

If you have multiple targets on the same element, you can chain them as there's also a stimulus_target filter:

1
2
3
4
5
6
7
8
<div {{ stimulus_target('controller', 'myTarget')|stimulus_target('other-controller', 'anotherTarget') }}>
    Hello
</div>

<!-- would render -->
<div data-controller-target="myTarget" data-other-controller-target="anotherTarget">
    Hello
</div>

You can also retrieve the generated attributes as an array, which can be helpful e.g. for forms:

1
{{ form_row(form.password, { attr: stimulus_target('hello-controller', 'myTarget').toArray() }) }}

Configuration

If you're using AssetMapper, you can configure the path to your controllers directory and the controllers.json file if you need to use different paths:

1
2
3
4
5
6
# config/packages/stimulus.yaml
stimulus:
    # the default values
    controller_paths:
        - '%kernel.project_dir%/assets/controllers'
    controllers_json: '%kernel.project_dir%/assets/controllers.json'

Manual Installation Details

When you install this bundle, its Flex recipe should handle updating all the files needed. If you're not using Flex or want to double-check the changes, check out the StimulusBundle Flex recipe. Here's a summary of what's inside:

  • assets/bootstrap.js starts the Stimulus application and loads your controllers. It's imported by assets/app.js and its exact content depends on whether you have Webpack Encore or AssetMapper installed (see below).
  • assets/app.js is updated to import assets/bootstrap.js
  • assets/controllers.json This file starts (mostly) empty and is automatically updated as your install UX packages that provide Stimulus controllers.
  • assets/controllers/ This directory is where you should put your custom Stimulus controllers. It comes with one example hello_controller.js file.

A few other changes depend on which asset system you're using:

With AssetMapper

If you're using AssetMapper, two new entries will be added to your importmap.php file:

1
2
3
4
5
6
7
8
9
10
11
// importmap.php
return [
    // ...

    '@symfony/stimulus-bundle' => [
        'path' => '@symfony/stimulus-bundle/loader.js',
    ],
    '@hotwired/stimulus' => [
        'version' => '3.2.2',
    ],
];

The recipe will update your assets/bootstrap.js file to look like this:

1
2
3
4
// assets/bootstrap.js
import { startStimulusApp } from '@symfony/stimulus-bundle';

const app = startStimulusApp();

The @symfony/stimulus-bundle refers the one of the new entries in your importmap.php file. This file is dynamically built by the bundle and will import all your custom controllers as well as those from controllers.json. It will also dynamically enable "debug" mode in Stimulus when your application is running in debug mode.

Tip

For AssetMapper 6.3 only, you also need a {{ ux_controller_link_tags() } in base.html.twig. This is not needed in AssetMapper 6.4+.

With WebpackEncoreBundle

If you're using Webpack Encore, the recipe will also update your webpack.config.js file to include this line:

1
2
// webpack.config.js
.enableStimulusBridge('./assets/controllers.json')

The assets/bootstrap.js file will be updated to look like this:

1
2
3
4
5
6
7
8
9
// assets/bootstrap.js
import { startStimulusApp } from '@symfony/stimulus-bridge';

// Registers Stimulus controllers from controllers.json and in the controllers/ directory
export const app = startStimulusApp(require.context(
    '@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
    true,
    /\.[jt]sx?$/
));

And 2 new packages - @hotwired/stimulus and @symfony/stimulus-bridge - will be added to your package.json file.

How are the Stimulus Controllers Loaded?

When you install a UX PHP package, Symfony Flex will automatically update your package.json file (not done or needed if using AssetMapper) to point to a "virtual package" that lives inside that PHP package. For example:

1
2
3
4
5
6
{
    "devDependencies": {
        "...": "",
        "@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/assets"
    }
}

This gives you a real Node package (e.g. @symfony/ux-chartjs) that, instead of being downloaded, points directly to files that already live in your vendor/ directory.

The Flex recipe will usually also update your assets/controllers.json file to add a new Stimulus controller to your app. For example:

1
2
3
4
5
6
7
8
9
10
11
{
    "controllers": {
        "@symfony/ux-chartjs": {
            "chart": {
                "enabled": true,
                "fetch": "eager"
            }
        }
    },
    "entrypoints": []
}

Finally, your assets/bootstrap.js file will automatically register:

  • All files in assets/controllers/ as Stimulus controllers;
  • And all controllers described in assets/controllers.json as Stimulus controllers.

Note

If you're using WebpackEncore, the bootstrap.js file works in partnership with @symfony/stimulus-bridge. With AssetMapper, the bootstrap.js file works directly with this bundle: a @symfony/stimulus-bundle entry is added to your importmap.php file via Flex, which points to a file that is dynamically built to find and load your controllers (see Configuration).

The end result: you install a package, and you instantly have a Stimulus controller available! In this example, it's called @symfony/ux-chartjs/chart. Well, technically, it will be called symfony--ux-chartjs--chart. However, you can pass the original name into the {{ stimulus_controller() }} function from WebpackEncoreBundle, and it will normalize it:

1
2
3
4
<div {{ stimulus_controller('@symfony/ux-chartjs/chart') }}>

<!-- will render as: -->
<div data-controller="symfony--ux-chartjs--chart">
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    Symfony Code Performance Profiling

    Symfony Code Performance Profiling

    Take the exam at home

    Take the exam at home

    Version:

    Table of Contents

    • Installation
    • Usage
      • TypeScript Controllers
      • The UX Packages
      • Lazy Stimulus Controllers
    • Stimulus Tools around the World
    • Stimulus Twig Helpers
      • stimulus_controller
      • stimulus_action
      • stimulus_target
    • Configuration
    • Manual Installation Details
      • With AssetMapper
      • With WebpackEncoreBundle
    • How are the Stimulus Controllers Loaded?

    Symfony footer

    Avatar of Vladimir Tsykun, a Symfony contributor

    Thanks Vladimir Tsykun (@vtsykun) for being a Symfony contributor

    19 commits • 319 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Powered by

    Follow Symfony