Setting up Webpack Encore

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

So let's get Webpack Encore installed so we can get a proper CSS and JavaScript build system set up.

Installing Encore

Find your terminal and run:

composer require encore

This really installs WebpackEncoreBundle, which will give us a few Twig helper functions. But its true superpower is its recipe.

The Encore Recipe Files

After Composer finishes, run:

git status

Ok: it modified several files, most of which make sense. The .gitignore file is now ignoring the node_modules/ directory and the bundle was automatically enabled in config/bundles.php.

There are also several new config files, which mostly aren't too important. What is important is that it created a package.json file. Let's go check that out. This defines several things that our Webpack Encore setup needs, like Encore itself and stimulus, which we'll talk more about later.

The other hugely important file the recipe added was webpack.config.js - with a basic setup. The recipe also added this assets/ directory. There are a few things in here related to Stimulus: the "controllers" stuff and bootstrap.js. We'll talk about those soon.

To download the dependencies described in package.json, find your terminal and run:

yarn install

Oh, and if you're totally new to Encore and want to dig into it deeper, check out our Webpack Encore: Frontend like a Pro! course.

The Entry Files and First Build

Right now, our app has a pretty standard Webpack Encore setup. We have one entry which is this assets/app.js file. That means that when we build our assets in a few minutes, Webpack will only look at this file to figure out all the JavaScript and CSS needed in the project. It will then output the final, built CSS and JavaScript files into a public/build/ directory.

Of course, when it reads this file, it will follow any imports, like this import of ./styles/app.css... which is a dummy file that sets the background-color to gray.

So, once we have executed Encore and built the assets - which we'll do in a minute - we're going to need script and link tags that point to those new files.

encore_entry_link_tags() & encore_entry_script_tags()

Let's add those to our base layout: templates/base.html.twig.

I already have a couple of link tags up here in the stylesheets block. Add {{ encore_entry_link_tags() }} and pass it app, which is name of that entry.

... lines 1 - 2
<head>
... lines 4 - 5
{% block stylesheets %}
... lines 7 - 9
{{ encore_entry_link_tags('app') }}
{% endblock %}
... lines 12 - 15
</head>
... lines 17 - 76

This will render the built version of the app.css file plus any other CSS that our JavaScript imports. And actually, for performance reasons, Webpack may split that built app.css into multiple files: we'll see that in a few minutes. This function takes care of including all the link tags we need.

Do the same thing in the javascripts block: {{ encore_entry_script_tags() }} and pass app.

... lines 1 - 2
<head>
... lines 4 - 12
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</head>
... lines 17 - 76

Like with the styles, this will render the built version of app.js plus any JavaScript it imports. And it may also be split into multiple files.

The New <script> defer Attribute

Oh, and you're seeing a recent change in Symfony's TwigBundle recipe for base.html.twig. The javascripts block used to live down here at the bottom of the page. Now it lives up in the head in new projects. Normally... that would not be a good idea, because when your browser sees a script tag, it stops rendering the page until it can download and execute that JavaScript.

But thanks to a new feature in WebpackEncoreBundle - you can see it in config/packages/webpack_encore.yaml, here it is script_attributes - every script tag rendered by Encore will have a defer attribute. That basically means that the JavaScript isn't executed until after the page loads, very similar to having the scripts at the bottom of the page. But with the new setup, our browser does start downloading the files slightly earlier. And this plays nicer with Turbo - the topic of our next tutorial.

If you want to learn more about this change, check out a blog post I wrote on Symfony.com.

Next: let's use Encore to build our assets, see "code splitting" in action and import a third-party CSS package.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=7.4.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "1.11.99.1", // 1.11.99.1
        "doctrine/annotations": "^1.0", // 1.11.1
        "doctrine/doctrine-bundle": "^2.2", // 2.2.3
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.8", // 2.8.1
        "phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
        "sensio/framework-extra-bundle": "^5.6", // v5.6.1
        "symfony/asset": "5.2.*", // v5.2.3
        "symfony/console": "5.2.*", // v5.2.3
        "symfony/dotenv": "5.2.*", // v5.2.3
        "symfony/flex": "^1.3.1", // v1.12.1
        "symfony/form": "5.2.*", // v5.2.3
        "symfony/framework-bundle": "5.2.*", // v5.2.3
        "symfony/property-access": "5.2.*", // v5.2.3
        "symfony/property-info": "5.2.*", // v5.2.3
        "symfony/proxy-manager-bridge": "5.2.*", // v5.2.3
        "symfony/security-bundle": "5.2.*", // v5.2.3
        "symfony/serializer": "5.2.*", // v5.2.3
        "symfony/twig-bundle": "5.2.*", // v5.2.3
        "symfony/ux-chartjs": "^1.1", // v1.2.0
        "symfony/validator": "5.2.*", // v5.2.3
        "symfony/webpack-encore-bundle": "^1.9", // v1.11.1
        "symfony/yaml": "5.2.*", // v5.2.3
        "twig/extra-bundle": "^2.12|^3.0", // v3.2.1
        "twig/intl-extra": "^3.2", // v3.2.1
        "twig/twig": "^2.12|^3.0" // v3.2.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.0
        "symfony/debug-bundle": "^5.2", // v5.2.3
        "symfony/maker-bundle": "^1.27", // v1.30.0
        "symfony/monolog-bundle": "^3.0", // v3.6.0
        "symfony/stopwatch": "^5.2", // v5.2.3
        "symfony/var-dumper": "^5.2", // v5.2.3
        "symfony/web-profiler-bundle": "^5.2" // v5.2.3
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@babel/preset-react": "^7.0.0", // 7.12.13
        "@popperjs/core": "^2.9.1", // 2.9.1
        "@symfony/stimulus-bridge": "^2.0.0", // 2.1.0
        "@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
        "@symfony/webpack-encore": "^1.0.0", // 1.0.4
        "bootstrap": "^5.0.0-beta2", // 5.0.0-beta2
        "core-js": "^3.0.0", // 3.8.3
        "jquery": "^3.6.0", // 3.6.0
        "react": "^17.0.1", // 17.0.1
        "react-dom": "^17.0.1", // 17.0.1
        "regenerator-runtime": "^0.13.2", // 0.13.7
        "stimulus": "^2.0.0", // 2.0.0
        "stimulus-autocomplete": "^2.0.1-phylor-6095f2a9", // 2.0.1-phylor-6095f2a9
        "stimulus-use": "^0.24.0-1", // 0.24.0-1
        "sweetalert2": "^10.13.0", // 10.14.0
        "webpack-bundle-analyzer": "^4.4.0", // 4.4.0
        "webpack-notifier": "^1.6.0" // 1.13.0
    }
}