Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Packaging JS and CSS with 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.

When we installed Webpack Encore, its recipe gave us this new assets/ directory. Check out the app.js file. Interesting. Notice how it imports this bootstrap file. That's actually bootstrap.js: this file right here. The .js extension is optional.

JavaScript Imports

This is one of the most important things that Webpack gives us: the ability to import one JavaScript file from another. We can import functions, objects... really anything from another file. We're going to talk more about this bootstrap.js file in a little bit.

This also imports a CSS file!? If you haven't seen this before, it might look... weird: JavaScript importing CSS?

To see how this all works, in app.js, add a console.log().

15 lines assets/app.js
... lines 1 - 12
console.log('Hi! My name is app.js!');

And app.css already has a body background... but add an !important so that we can definitely see if this is being loaded.

body {
background-color: lightgray !important;

Ok... so who reads these files? Because... they don't live in the public/ directory... so we can't create script or link tags that point directly to them.


To answer that, open webpack.config.js. Webpack Encore is an executable binary: we're going to run it in a minute. When we do, it will load this file to get its config.

And while there are a lot of features inside of Webpack, the only thing we need to focus on right now is this one: addEntry(). This app could be anything... like dinosaur, it doesn't matter. I'll show you how that's used in a minute. The important thing is that it points to the assets/app.js file. Because of this, app.js will be the first and only file that Webpack will parse.

It's pretty cool: Webpack will reads the app.js file and then follow all of the import statements recursively until it finally has a giant collection of all the JavaScript and CSS our app needs. Then, it will write that into the public/ directory.

Running Webpack Encore

Let's see it in action. Find your terminal and run:

yarn watch

This is, as it says, a shortcut for running encore dev --watch. If you look at your package.json file, it came with a script section with some shortcuts.

Anyways, yarn watch does two things. First, it created a new public/build/ directory and, inside, app.css and app.js files! But don't let the names fool you: app.js contains a lot more that just what's inside assets/app.js: it contains all the JavaScript from all the imports it finds. app.css contains all the CSS from all the imports.

The reason these files are called app.css and app.js is because of the entry name.

So the takeaway is that, thanks to Encore, we suddenly have new files in a public/build/ directory that contain all the JavaScript and CSS our app needs!

The Encore Twig Functions

And if you move over to your homepage and refresh... woh! It instantly worked!? The background changed... and in my inspector... there's the console log! How the heck did that happen?

Open your base layout: templates/base.html.twig. The secret is in the encore_entry_link_tags() and encore_entry_script_tags() functions. I bet you can guess what these do: add the link tag to build/app.css and the script tag to build/app.js.

You can see this in your browser. View the source for the page and... yup! The link tag for /build/app.css... and script tag for /build/app.js. Oh, but it also rendered two other script tags. That's because Webpack is really smart. For performance purposes, instead of dumping one gigantic app.js file, sometimes Webpack will split it into multiple, smaller files. Fortunately, these Encore Twig functions are smart enough to handle that: it will include all the link or script tags needed.

The most important thing is that the code that we have in our assets/app.js file - including anything it imports - is now functioning and showing up on our page!

Watching for Changes

Oh, and because we ran yarn watch, Encore is still running in the background watching for changes. Check it out: go into app.css... and change the background color. Save, move over and refresh.

body {
background-color: maroon !important;

It instantly updated! That's because Encore noticed the change and recompiled the built file really quickly.

Next: let's move our existing CSS into the new system and learn how we can install and import third party libraries - look Bootstrap or FontAwesome - right into our Encore setup.

Leave a comment!

Login or Register to join the conversation
Rufnex Avatar

Just a short question about the javascript embedding. Should it embed at the end oft the html instead of the head areas?

victor Avatar victor | SFCASTS | Rufnex | posted 9 days ago | HIGHLIGHTED

Hey Rufnex,

Good question! For many years it was recommended to include your JS at the end of your body tag so that them do not block the page rendering and page loads faster. But lately things changed, and now it's recommended to include all your JS/CSS in the head of the page again... but adding the defer attribute to it. Check a few screencasts to learn more about it:

As you can see, Webpack Encore already knows how to handle it, you just need to configure it.


2 Reply
Rufnex Avatar

Thank you for your great Input. Now i've got it ;o)


Hey Rufnex,

Perfect! I'm glad to hear it ;)


1 Reply

although the site works correctly but they show me these errors in the console


Hello, i have JS error in console,

Uncaught (in promise) TypeError: Cannot destructure property 'eligibleURLPatterns' of 'Jt' as it is undefined companion-bubble.js:1436
at companion-bubble.js:1436:11909
at Generator.next (<anonymous>)
at tn (companion-bubble.js:1436:11431)

Uncaught TypeError: Cannot read properties of null (reading 'style') (index):212
at renderAjaxRequests ((index):212:10065)
at finishAjaxRequest ((index):212:15943)
at (index):212:21834
at xhr.onreadystatechange ((index):212:7896)

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'style') (index):212
at renderAjaxRequests ((index):212:10065)
at finishAjaxRequest ((index):212:15943)
at (index):212:18218

Petr L. Avatar

Hello, I feel like there's something I'm doing wrong, but I simply cannot figure out what it is; the background color of the page won't change regardless of what I do, it always stays "lightgrey" and keeps overwriting the custom app.css that I copied earlier from the tutorial folder.
The issue I'm facing specifically is that even when I update assets/styles/app.css to a different color (let's say "maroon"), it stay lightgrey, even though the "maroon" is stated in app.css in the /build directory. According to the developer tools, the file that overwrites the color of <body> stems from webpack///assets/styles/app.css. I also noticed that the console log specified in app.js will never go through either, the console stays empty.
Everything else works as intended, yarn apparently works, but I've had this default lightgrey color there from the very beginning of the whole course and I can't figure out how to rewrite it.


Hi Petr Louda!

Apologies for the slow reply! This is super weird... it's as if your code is "stuck" with the original code. That's especially true since you said the console.log in app.js also doesn't seem to work.


> According to the developer tools, the file that overwrites the color of <body> stems from webpack///assets/styles/app.css.

Encore publishes a sourcemap, which helps map the styles to the "source" filename. So in case you were wondering why it doesn't say "build/app.css", that's why. It's loading it from build/app.css... but then the sourcemap tells it that the true source is assets/styles/app.css. But this doesn't answer what's wrong at all - just mentioning this.

Ok, so let's do some debugging! You mentioned that:

> even though the "maroon" is stated in app.css in the /build directory

That's a good detail to check into. To be absolutely sure, stop Encore, completely empty the public/build directory, then re-run Encore. NOW look inside there again. Does the public/build/app.css file have maroon inside of it or lightgrey?

What happens if you make a JavaScript syntax error in assets/app.js? You should get an error in your console from Encore. Do you? Or not?

Finally, triple check that you browser isn't loading out-of-date content by doing a force refresh (or opening the site in incognito mode).

Let me know what you find out - something is definitely wrong, but I bet it's a minor thing (the minor things are the hard ones to track down!).


Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "symfony/asset": "6.0.*", // v6.0.3
        "symfony/console": "6.0.*", // v6.0.3
        "symfony/dotenv": "6.0.*", // v6.0.3
        "symfony/flex": "^2", // v2.1.5
        "symfony/framework-bundle": "6.0.*", // v6.0.4
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.3
        "symfony/twig-bundle": "6.0.*", // v6.0.3
        "symfony/ux-turbo": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.3
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.8
        "twig/twig": "^2.12|^3.0" // v3.3.8
    "require-dev": {
        "symfony/debug-bundle": "6.0.*", // v6.0.3
        "symfony/stopwatch": "6.0.*", // v6.0.3
        "symfony/web-profiler-bundle": "6.0.*" // v6.0.3