Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Lazy Stimulus Controllers

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

It's getting messy in here: let me close a few files... then crack open assets/controllers/goodbye-controller.js. Pretend, for a moment, that this controller is huge. Or, more likely, it imports a big third-party package like d3 for charts. But, we're only using this controller on some pages.

Here's the deal. In order to register your controllers with Stimulus, all of these files are downloaded immediately. So the page loads, Stimulus starts up, all of these files are downloaded, and any files they import are also downloaded. That's often ok, but if you're importing something big, that can be wasteful.

To fix that, above the class, you can add a very special syntax - /* stimulusFetch: 'lazy' */.

... lines 1 - 2
/* stimulusFetch: 'lazy' */
export default class extends Controller {
... lines 5 - 7

This works thanks to StimulusBundle. When it spots this, it tells Stimulus to hold its horses and not download this JavaScript file or anything it imports until an element that matches this is on the page.

Watch. Before making that change, if we searched for "goodbye", that controller was being loaded, even though it's not used on this page. But now, refresh and search for "goodbye". It's not there! Inspect the data-controller="hello" element. Change that to goodbye and... boom! It works! You can see that it activated (that's what our Goodbye controller! does), and if we look at the Network tab, now it downloaded. I love this feature.

This can also be done for third-party packages. If you look in assets/controllers.json... Turbo isn't a very good example of this, but if we said "fetch": "lazy" on any of these, they would have the same behavior that we just saw.

"controllers": {
"@symfony/ux-turbo": {
"turbo-core": {
"enabled": true,
"fetch": "eager"
... lines 8 - 11
... line 14

That's it! Easiest chapter ever! Use this to keep your initial page lightweight if you have some heavy Stimulus controllers that are only used on certain page.

Next: sometimes, deep sigh, the tech gods frown upon us and things don't work. Let's learn a few tips to help debug when that happens.

Leave a comment!

Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^4.0", // v4.2.0
        "doctrine/doctrine-bundle": "^2.7", // 2.10.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.4
        "doctrine/orm": "^2.12", // 2.15.2
        "knplabs/knp-time-bundle": "^1.18", // v1.20.0
        "pagerfanta/doctrine-orm-adapter": "^4.0", // v4.1.0
        "pagerfanta/twig": "^4.0", // v4.1.0
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.1
        "symfony/asset": "6.3.*", // v6.3.0
        "symfony/asset-mapper": "6.3.*", // v6.3.0
        "symfony/console": "6.3.*", // v6.3.0
        "symfony/dotenv": "6.3.*", // v6.3.0
        "symfony/flex": "^2", // v2.3.1
        "symfony/framework-bundle": "6.3.*", // v6.3.0
        "symfony/http-client": "6.3.*", // v6.3.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "6.3.*", // v6.3.0
        "symfony/runtime": "6.3.*", // v6.3.0
        "symfony/stimulus-bundle": "^2.9", // v2.9.1
        "symfony/twig-bundle": "6.3.*", // v6.3.0
        "symfony/ux-turbo": "^2.9", // v2.9.1
        "symfony/web-link": "6.3.*", // v6.3.0
        "symfony/yaml": "6.3.*", // v6.3.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.6.1
        "twig/twig": "^2.12|^3.0" // v3.6.1
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.4
        "symfony/debug-bundle": "6.3.*", // v6.3.0
        "symfony/maker-bundle": "^1.41", // v1.49.0
        "symfony/stopwatch": "6.3.*", // v6.3.0
        "symfony/web-profiler-bundle": "6.3.*", // v6.3.0
        "zenstruck/foundry": "^1.21" // v1.33.0