Login to bookmark this course

Webpack Encore: Frontend like a Pro!

Boost your JS coding with this Webpack Encore tutorial. Overcome JS limitations and start writing better code.

  • 4202 students
  • EN Captions
  • EN Script
  • Certificate of Completion

Your Guides

About this course

This tutorial works great with Symfony5!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "aws/aws-sdk-php": "^3.87", // 3.91.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/annotations": "^1.0", // 1.10.1
        "doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
        "doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
        "doctrine/orm": "^2.5.11", // v2.7.2
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
        "knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
        "knplabs/knp-time-bundle": "^1.8", // 1.9.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "liip/imagine-bundle": "^2.1", // 2.1.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
        "oneup/flysystem-bundle": "^3.0", // 3.0.3
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
        "sensio/framework-extra-bundle": "^5.1", // v5.3.1
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.2.5
        "symfony/console": "^4.0", // v4.2.5
        "symfony/flex": "^1.9", // v1.21.6
        "symfony/form": "^4.0", // v4.2.5
        "symfony/framework-bundle": "^4.0", // v4.2.5
        "symfony/property-access": "4.2.*", // v4.2.5
        "symfony/property-info": "4.2.*", // v4.2.5
        "symfony/security-bundle": "^4.0", // v4.2.5
        "symfony/serializer": "4.2.*", // v4.2.5
        "symfony/twig-bundle": "^4.0", // v4.2.5
        "symfony/validator": "^4.0", // v4.2.5
        "symfony/web-server-bundle": "^4.0", // v4.2.5
        "symfony/webpack-encore-bundle": "^1.4", // v1.5.0
        "symfony/yaml": "^4.0", // v4.2.5
        "twig/extensions": "^1.5" // v1.5.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.7
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/debug-bundle": "^3.3|^4.0", // v4.2.5
        "symfony/dotenv": "^4.0", // v4.2.5
        "symfony/maker-bundle": "^1.0", // v1.11.5
        "symfony/monolog-bundle": "^3.0", // v3.3.1
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.5
        "symfony/stopwatch": "4.2.*", // v4.2.5
        "symfony/var-dumper": "^3.3|^4.0", // v4.2.5
        "symfony/web-profiler-bundle": "4.2.*" // v4.2.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.27.0", // 0.27.0
        "autocomplete.js": "^0.36.0",
        "autoprefixer": "^9.5.1", // 9.5.1
        "bootstrap": "^4.3.1", // 4.3.1
        "core-js": "^3.0.0", // 3.0.1
        "dropzone": "^5.5.1", // 5.5.1
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.4.0", // 3.4.0
        "popper.js": "^1.15.0",
        "postcss-loader": "^3.0.0", // 3.0.0
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^7.0.1", // 7.3.1
        "sortablejs": "^1.8.4", // 1.8.4
        "webpack-notifier": "^1.6.0" // 1.7.0
    }
}

Writing great PHP code but your JavaScript looks a little bit hacky? It's not your fault! Before Webpack, coding "correctly" in JavaScript... sorta wasn't possible!

But setting up Webpack can be a pain. So... hello Webpack Encore! Encore allows you to modern JavaScript starting... immediately! Go, go go!

  • Installing Encore & yarn
  • webpack.config.js & your first Webpack build!
  • Adding script & link tags thanks to WebpackEncoreBundle: encore_entry_script_tags() & encore_entry_link_tags()
  • Modules! require, import and export
  • Installing & using external libraries
  • jQuery plugins & global variables
  • Handling CSS! Including fonts & images
  • Code splitting: free, smart optimization
  • Sass & Overriding Bootstrap Sass Variables
  • Copying static files
  • Multiple "entries": page-specific CSS & JavaScript
  • PostCSS, Bower, browserslist, polyfills & new features in old browsers
  • enableSingleRuntimeChunk()?
  • Async imports!

Next courses in the JavaScript Frameworks & Tools: Build Systems section of the JavaScript Frameworks & Tools Track!

55 Comments

Sort By
Login or Register to join the conversation

I move an existing React project on a Symfony project using webpack and this project has a jsconfig.json with

{
     "compileOptions": {
         "baseUrl": "src"
     },
     "include": ["src"]
}

can I indicate this file in my webpack.config.js ? Thanks in advance

| Reply |

Hey @aratinau!

I'm not familiar with the jsconfig.json file - it looks like it is a config file that is ready by VSCode, is that right? If so, then my understanding is that nothing (other than your editor) reads this file. So you can have it just like you did before, and your editor should use it.

Or maybe you're really asking to set these same settings inside of Encore? I'm not sure - as I'm not too familiar with these settings.

Sorry I can't be more helpful!

| Reply |

Hey @weaverryan!

Thanks for the reply! It's not a config file for my editor, it's to indicate to the React compilation that the files start at the root of the project, to avoid the relative links like "../../../utils.js" and to be able to indicate just "utils". I found a solution on webpack but it forces me to use a character, in my case "~". (character that I did not have to put using react-scripts)

I asked the question https://github.com/symfony/webpack-encore/issues/37#issuecomment-1552882276

| Reply |

Hey @aratinau!

Ok, I understand a bit better :). Since Encore is just a wrapper around Webpack, the solution is to "do however you would do this in Webpack", which it sounds like you've found... but you maybe aren't satisfied by. But that's the right way - even if Encore doesn't have a shortcut for something, you can always add the config at the end (like earlier comments on the webpack-encore issue show).

Cheers!

1 | Reply |

I have followed this tutorial twice. Not to mention the Symfony 4 version. All my JS. There are some things missing because I have tried for 3 days to make my JS work with web pack. Sure, they build, but nothing works anymore as before when the JS were outside. Bootstrap 5 + jQuery, the docs in the official Symfony docs have a different way to include jQuery. Yet, I don't think that even matters. jQuery seems to work fine even without adding them to your JS asset files. The global variable does nothing either. jQuery seems to be enabled inside the build files, but you can't access from Twig or any outside scripts. I have tried all possible ways.

The worst part is that there is no clear explanation on how to turn regular JS files into web pack compatible files. They clearly do not work as expected, if they even work at all. My basic theme basically breaks completely, and I have a lot of external JS, which means I need to have some directly in Twig. The what web pack does seems great, but it breaks way too many things. Why can't you just put a JS file into web pack and make it work like a normal script? And there is also no clear explanation on the order they render in the build generated file. I need to have specific JS vendor files in specific orders yet this is not explained at all, the files are just one big build. I tried setting them into different build entry points and out those entry points in order in my Twig base template. No different. The worst part is that with a normal JS you can check the error in the browsers console. Once they are in web pack, there are no errors in the browser console. You can't check what stopped working or why.

Why is this so hard to get with something that is so widely used like Bootstrap with jQuery? Extremely frustrated to the point I'm giving up completely on web pack encore.

| Reply |

Hey n0!

Well this definitely is not the experience we want anyone to have!

The worst part is that there is no clear explanation on how to turn regular JS files into web pack compatible files

Yea, to be honest, converting "traditional JavaScript" into something at works in Webpack is non-trivial. It's a totally different paradigm, and so it breaks a lot of the assumptions we used when writing that original JavaScript. We converted at Symfonycasts over a long period of time, little-by-little.

The what web pack does seems great, but it breaks way too many things. Why can't you just put a JS file into web pack and make it work like a normal script?

We DO talk about some of this, but in an even older tutorial :/. It's https://symfonycasts.com/screencast/javascript-webpack/legacy-global-jquery and https://symfonycasts.com/screencast/javascript-webpack/global-jquery

The biggest problem with our legacy JavaScript is that it assumes certain global variables - like $. So, the way to migrate is to make sure that any global variables you expect remain as global variables for now. For jQuery, for example, the way to do that is to go into assets/app.js, import jquery, then set it on a global variable:

// assets/app.js
// this is the first file read by Webpack/Encore, so the code you put on top will be executed first

// this imports $ into a local variable, but it doesn't set $ as
// a global variable like outside of Webpack
import $ from 'jquery';

// this makes `$` and `jQuery` global variables like they were previously
global.$ = $;
global.jQuery = $;

// ...

Next, you'll need to make sure that your encore_entry_script_tags() code lives inside your head above any legacy JS. Also, make sure that you do NOT have the defer attribute on the script tags for now. So, set this to false: https://github.com/symfony/recipes/blob/e81834cbeace01f50e7da5b0db17d60a85209a22/symfony/webpack-encore-bundle/1.10/config/packages/webpack_encore.yaml#L9 - this will make all of your script tags download and parse in a "serial" way, like you're used to. The result is this:

A) Because you have encore_entry_script_tags('app') in head above your other JS, the code in assets/app.js (well, to be precise, the built version of this code) will be executed first. This will result in $ becoming a global variable.
B) The rest of your JS then runs like normal.

Then, over time, you can, little-by-little refactor non-Webpack JavaScript (which might live in external .js files or inline in your templates) into proper weback files.

And there is also no clear explanation on the order they render in the build generated file

I know this is probably not a satisfying answer, but this is one of those things that don't matter with the Webpack paradigm, since it doesn't rely on global variables (if you have global variables, then you need foo.js to be parsed first because it sets a Foo global variable and then you need to bar.js to be parsed second, because it relies on the Foo variable. But once all files are built for Webpack, order doesn't matter because bar.js would have import Foo from './foo').

BUT, yes, for legacy JS, order still matters. If you have, for example, 5 <script> tags for 5 vendor files, I would remove those but then put them at the top of your assets/app.js file in the correct order. If they are truly not written to support Webpack, then you don't even need to import a value from them: you're just importing them so that they're included:

import './foo';
import './bar';

One word of caution: sometimes (jQuery is a perfect example) if you include a vendor JS library via a <script> tag, it detects this and creates a global variable (e.g. $). But when you import that same file from Webpack, that library detects that and, instead, it does NOT set a global variable but instead "exports" a value. If you have this situation, do the same thing we did above with jQuery: import the value and set it as a global variable:

import Foo from 'foo';
global.Foo = Foo;

The worst part is that with a normal JS you can check the error in the browsers console. Once they are in web pack, there are no errors in the browser console. You can't check what stopped working or why.

Hmm. This shouldn't be the case. At the end of the day, Webpack parses assets/app.js (assuming you're using just one app entrypoint) and executes all the code in there (including imports) in a "serial" way: line-by-line. It builds and splits all of this into multiple files for performance reasons, but when your browser runs the code, it runs as if it's parsing and executing assets/app.js line-by-line, just like traditional JavaScript. So if you're not getting an error, it's telling me that the code you're expecting to work/error is not being included at all somehow.

Why is this so hard to get with something that is so widely used like Bootstrap with jQuery? Extremely frustrated to the point I'm giving up completely on web pack encore.

Yea, it's SUCH a paradigm shift, and refactoring from legacy code is MUCH harder than starting from scratch. For an analogy, it would be like taking PHP code from 15 years ago (so before Composer) where your code uses require_once statements and looks nothing like modern PHP... then needing to take this code and get it working inside a modern app. That would be a huge pain. But starting with a modern PHP app with Composer is super easy and nice. So, feeling frustrated I think is realistic, because what you're trying to do is tricky! Keep at it, and I'm happy to help with any questions.

Cheers!

| Reply |

I was sadly not aware that code in JS has to work differently than the traditional way of loading the sources from a web page when I started refactoring. I understand this is how node modules work now. Furthermore, I initially imagined WebPack just does what it names says, pack scripts into one big one, not that it actually requires a different approach on how every JS source code has to be written and scripts with each other. Most web JS file except to be loaded on the page they are being executed.

Now, it seems some of the problems came because of how jQuery + Boostrap work together. Example, I use the jQuery + Boostrap 5 import in the app.js file, but since both bootstrap requires both CSS on the top of a page, and then JavaScript at the bottom, I of course added bootstrap imports as well in the app.css for the Bootstrap CSS code but also the in app.js for the bootstrap JavaScript files.

This hast caused alot of grief. On my tests when I remove bootstrap from app.css of course the page breaks (no bootstrap anymore) but I had no idea that I only need to import this once (it seems) and only on app.css. The reason is that in the official Symfony docs, they add jQuery and bootstrap imports in the code and this just is wrong.

The second I removed the import bootstrap from app.js and only left it in the app.css only, then big chunks of my site started working again. There was no error in the browser console, but this is not explained anywhere in the docs. I only figured this out after a lot of testing, it seems bootstrap was importing twice? In the app.js and app.css?

If I remove bootstrap from the CSS file and only leave it in the JS app it does not work, but that is what the official Symfony docs say you are supposed to do:
https://symfony.com/doc/current/frontend/encore/bootstrap.html

They import both bootstrap styles and bootstrap JS code. This does breaks my app. If I only import bootstrap from the installed node yarn package in app.css but not in app.js it seems work. I do leave jquery enabled in app.js

As for the jQuery function, it makes no difference if I leave it in app.js, it seems jQuery works either way which is probably why the global function does nothing either in my case. I don’t know if this is some web pack bug or not, but I added a jQuery script code in app.js that prints a message if jQuery is enabled in the browsers console, I installed both bootstrap and jQuery with yarn, if I remove the jQuery import from my files, the code still tells jQuery is enabled fine. It seems it is being build either way, even if I do not specify jQuery in the app.js.

This would also explain why the global $ jQuery variable has no effect. I restarted yarn watch after changing settings, yet I don’t see it actually doing anything at all.

External scripts can’t access jQuery regardless of anything I put in the app.js config file which tells me that is not actually controlling the jQuery that is used by web pack by symfony.

Yes, I have the proper entry script tags and above legacy script, no defer either on any.

Maybe you should record a short tutorial about how to properly pack legacy JS files into web pack JS files. At least basic examples because I think mixing scripts, in web pack and outside web pack is not a good idea it seems its an all web pack approach or no web pack, at least I can't seem to find how to mix old JS with web pack code. And the reason I did this is that some scripts are not available as yarn packages and I still need them. Modern JS probably has a nodule version, but either way I could not make it work if I use them as a node module but seem to work if I use them externally. Example, modernizer, I have this directly as modernizer.js in my templates, but it actually does not seem to work properly if I change it to be used in a node module.

| Reply |

Hey!

Yup, you're right that this is a big thing. It's a "good" big thing, but you're 100% correct that it's not just a "bundles"
of code.

The reason is that in the official Symfony docs, they add jQuery and bootstrap imports in the code and this just is wrong

Bootstrap is a tricky library because it provides both CSS and JS... and you can just use the CSS or the JS and CSS. Anyways, when you import 'bootstrap' form inside a .js file, that loads the Bootstrap JS. If you import '~bootstrap' from a CSS file, that imports the Bootstrap CSS. So, if you just want the CSS, then you only need the import in the CSS file. If you ALSO want the Bootstrap JS, that's when you need that other import.

As for the jQuery function, it makes no difference if I leave it in app.js, it seems jQuery works either way which is probably why the global function does nothing either in my case

Hmm. I agree with you that something smells funny here. It may be that there are multiple jQuery's being loaded. So, let's see if we can debug. If you do NOT import jquery from app.js (or anywhere else), when you load the page and open your console, what does console.log($) give you? Is it undefined (that's what we would expect) or does it print something? I'm wondering if, when you import jquery in app.js, that IS setting a global $ variable... but then something else is overriding it - possibly something in the legacy js.

I think mixing scripts, in web pack and outside web pack is not a good idea it seems its an all web pack approach or no web pack, at least I can't seem to find how to mix old JS with web pack code.

I think you CAN mix the two, and you should while updating your app (simply because refactoring ALL of your JS at once is a HUGE job - too huge). I think once you sort out some of the weird global variable problems, things will start to fall into place nicely. The trick is basically to "boot up" your global variables in app.js correctly. Once you do this (and it can be tricky, as we've seen!), the legacy js should happily work and then you can refactor little-by-little.

Example, modernizer, I have this directly as modernizer.js in my templates, but it actually does not seem to work properly if I change it to be used in a node module.

You're totally right. This sounds like the same thing that happens with jQuery. If you have a script tag in your template, then that .js file will add a global variable. But if you import it from inside a Webpack file, it will NOT set the global variable. Since your legacy JS is relying on a global Modernizr variable, this is yet another you can setup as a global var in app.js (and later remove once you're not using that global var anymore):

// assets/app.js
import Modernizr from 'modernizr';

global.Modernizr = Modernizr;

Btw, I don't know for sure if Modernizr works like this, but that's what it sounds like :). Also, be aware of possible version differences - e.g. it's possible that you might have a script tag for Modernizr 2, but then Modernizr 3 is installed when you add it via yarn.

Anyways, I hope this gets you closer!

| Reply |

Yes, that is precisely what they say here in the docs:
https://symfony.com/doc/current/frontend/encore/bootstrap.html

Except something breaks when I do that.

In my app.css I have:
// any CSS you import will output into a single css file (app.css in this case)<br />import './styles/app.css';

// start the Stimulus application<br />import './bootstrap';

const $ = require('jquery');<br />// uncomment if you have legacy code that needs global variables<br />//global.$ = $;

require('bootstrap');

That seems to break something. If I remove:
require('bootstrap');

It works.

In my original code, I'm supposed to import:
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css">

And as script:
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

Now I noticed there is no CSS in the node module and the symfony docs instead has an example for SCSS (which also seems wrong as it can't find anything). But the CSS styles are not the issue in this case. The bootstrap JS seems to be the issue because, but I don't actually care about that issue anymore but the jQuery one. Since my theme relies on jQuery I assume once that is fixed things would probably work as they suppose, because Bootstrap itself requires jQuery to work.

As for jQuery. If I keep all my scripts in web pack, I get no errors in the console. With or without the jQuery global $ commented or not. No errors but they don't seem to work correctly which is what I want to put some outside web pack and directly on the twig template, that way I can slowly move them to web pack as I keep testing things. Except I can't do this because jQuery is not available outside the scripts build with web pack. I tried everything and nothing works to get jQuery to work with scripts in Twig templates. My idea is simple, things that work in node, move them to web pack, things that don't, keep them on twig until I can refactor them.

If I move some of my main JS out of webpack and directly to the base Twig. With the:
//global.$ = $;

I get jQuery is not defined errors in some scripts.

If I then uncomment it:
global.$ = $;

Rebuild web pack.

I still get the same error, jQuery is not defined. The global $ makes no difference.

If I then put this code in app.js:
if (typeof jQuery != 'undefined') {<br /> console.log('jQuery is enabled');<br />} else {<br /> console.log('jQuery is not enabled');<br />}<br />

I get jQuery is enabled in the console. If I move the same code to the twig template, I get:
jQuery is not enabled even with global.$ enabled. Like I said in a previous comment, that variable seems to have absolutely no effect in my installation, which I suspect is the main reason for the issues.

Should maybe that global variable go to the bottom of the app.js? Or after the requirement bootstrap? The issue in my case seems that I cannot use jQuery from other scripts or twig. It only works inside JS files that are inside web pack. Using Symfony 6 and jQuery 3.6.3 installed with yarn.

I don't import jQuery from other sources. Even if I only have jquery and nothing else in my app.js I get that error, without actually loading a simple script from twig or web pack. I have no idea if there is a bug with symfony, node, web pack or encore but what jQuery is not available outside webpack in my installations.

| Reply |

I figured the bootstrap issue.

It seems the requirement bootstrap from the symfony docs is not needed in my case because I was already importing the bootstrap bundle directly from the node module:
import 'bootstrap/dist/js/bootstrap.bundle';

This was probably the issue I mentioned that when I add bootstrap to app.js it does not work.

But I had no luck with jQuery making it available outside of web pack on Twig scripts.

| Reply |

Hey n0!

Ah, good work with bootstrap. And, indeed, I see the problem now. The import './bootstrap' that you saw earlier has nothing to do with the Bootstrap library: it's referring to an assets/bootstrap.js file that you get if you install WebpackEncore and get its recipe. It's this file right here - https://github.com/symfony/recipes/blob/main/symfony/webpack-encore-bundle/1.10/assets/bootstrap.js - and its purpose is to initialize the Stimulus JavaScript library (a nice library, but totally optional). Sorry that caused confusion!

About jQuery, my guess is that the problem is something subtle - just some small crossed wires. The process of how all the files are loaded is perfectly straightforward (meaning, there is nothing fancy happening - as long as the defer attribute is not on your script tags, and you said it wasn't). If you view the HTML source on your page, your browser is executing each <script src="/???.js"> file one-by-one. So, if the script tags for app.js (and the other files that it's split into - i.e. the stuff from encore_entry_script_tags('app') are the first script tags on the page, then this means that the the first JS running on your page is the top of assets/app.js . That's why (and yea, I realize it's not working yet), we're putting global.$ = $ at the top of this file. Then, if you look back at the HTML source of your page, if you have any inline <script> tags that come later, that JS should be executed later. You could triple-check this by adding a console.log('app.js') in assets/app.js and another console.log('inline') in an inline script tag in your Twig template to verify that app.js prints first and then inline.

But, I'm still not sure what's going wrong, though I do want to point out one thing just in case. When you have this:

// assets/app.js
import $ from 'jquery'
global.$ = $;

That creates a global variable called $. It does NOT also create a global variable called jQuery. If you want to add both (I often do - because both ways are valid), then use this:

import $ from 'jquery'
global.$ = $;
global.jQuery = $;

To see if this is working, I would do this in base.html.twig : find your {{ encore_entry_script_tags('app') }} and add a script tag right after it:

<!-- templates/base.html.twig -->

...

{{ encore_entry_script_tags('app') }}
<script>
console.log($, jQuery);
</script>

In theory, that should print some function for both. If it's not working, both should print undefined.

Anyways, let me know if any of this helps - I'm quite sure it's some small detail.

Cheers!

| Reply |

Thanks Ryan, about the bootstrap issue, I was not confusing it with the stimulus bootstrap. I was actually importing the bundle bootstrap before, which means it was importing it twice, but at least that is out of the picture.

As for jQuery, your trick about adding the console log might explain the issue. Indeed, inline console is executed first before app.js which explains why the jQuery variables are not found.

Now, my encore entry link tags are before above and on top of that code in twig.
{{ encore_entry_link_tags('app') }}

<script>
        //console.log($, jQuery);

        console.log('inline');
</script>

At least I found the issue. It seems, scripts in Twig are executed first in the rendering process and then the encore script tags.

In the console I get:
inline
app.js

And then the stimulus application starts messages. How do I make app.js execute first? Because the order does not seem to have any effect on my tests.

| Reply |

Hey n0!

Ok, we're close now :). Forgetting about Twig for a moment, what does the actual, rendered HTML look like with regard to the Encore script tags and inline script tags (I mean, if you view the HTML source in your browser)? I'm looking for two things specifically:

A) Triple-checking that, in the real, final output HTML, the Encore <script> tags are above the inline <script> tags
B) Tripe-checking that no defer attributes have snuck into the <script> tags.

Encore doesn't do anything special to the way that your browser handles script tags. That process is perfectly boring: it reads and executes each script tag from top to bottom, in that order, one-by-one.... unless you have something like defer in your script tag. That's why I want to see the final, rendered HTML just to be absolutely sure. Apart from the order and attributes like defer or async, I can't think of any reason why these would be rendering in the opposite order of what we want.

Cheers!

| Reply |

The rendered app.js is rendered above the jQuery console tag but now that I see, web pack encore build is rendered with defer for all scripts, even when I don't have any script with defer as import in my app.js file.

This is how it is rendered:

    <script src="/build/runtime.js" defer></script><script src="/build/vendors-node_modules_jquery_dist_jquery_js.js" defer></script><script src="/build/vendors-node_modules_symfony_stimulus-bridge_dist_index_js-node_modules_bootstrap-datepicker_-e2378f.js" defer></script>
        
    <script>
        //console.log($, jQuery);

        console.log('inline');
    </script>

My imports are not with defer in place, but directly from the node. And I don't have any scripts in the twig template either, I removed all of them.

I use for example:

import 'bootstrap/dist/js/bootstrap.bundle';
import 'bootstrap-datepicker';
| Reply |
n0 avatar n0 n0 1 year ago edited

After reading this:
https://symfony.com/blog/moving-script-inside-head-and-the-defer-attribute

This is enabled in my config:

    script_attributes:
        defer: false

I never changed this, so I can only assume it was enabled by default after installing web pack encore. Now, app.js does execute before the Twig template and going to try to see if that solves the jQuery issue.

| Reply |

Hey n0!

Yay! Yes, that's it! Yes, we enable this by default because it's better for performance and works beautifully as long as all of your code is inside Webpack code. Sorry it took us so long to find this - I had asked about defer earlier - https://symfonycasts.com/screencast/webpack-encore#comment-28659 - but I don't think I was clear enough in that comment!

Anyways, onwards and upwards :).

Cheers!

| Reply |
Yangzhi avatar Yangzhi 2 years ago

if i want add global function or const in app.js.the call them on twig,how write it?

| Reply |

Hey Yangzhi,

We have a screencast about it in this course. Please, check it out here: https://symfonycasts.com/screencast/webpack-encore/external-libs

I hope this helps!

Cheers!

| Reply |

is there any way to import a webpack structure from a third party theme ?

| Reply |

Hey Paskuale

Could you please add more details? What exactly do you mean? Maybe some example?

Cheers!

| Reply |

I mean if I find a nice bs5 template with webpack installation, I can try to load it, to tell the truth I spent the weekend and it seems to work, really great.

| Reply |

oh... I think I got what do you mean. As I know that can't be done automatically, so it requires some manual work. You should include all stuff you need to your app.js and install all dependencies which are required by this template.

| Reply |

You are absolutely right, in fact it was not immediate, I lost 2 days to understand all the sub-functions of the webpack, and a lot of yarn add {packageName} but I was able to take advantage of an excellent theme (which due to the webpack system I could no longer to integrate it with another php framework) :)

| Reply |

Sounds like that it was a cool challenge that gives an awesome experience! Nice that you was able to achieve it!

Sorry that it took some time to answer your question! If you have more questions do not hesitate to ask!

Cheers!

| Reply |

On the contrary, you were very quick in answering me, asking a question before a weekend it is normal that we all rest on Saturday and Sunday, I take this opportunity to thank you for this splendid videocast in a week I will start working with simfony 5.x (I come from almost 20 years of work with my beloved fw that I don't quote here for not doing OT) and thanks to these videocasts I'm learning many interesting things. ;)

| Reply |

Thank you very much for your feedback! This is the best reward for us.

Stay safe and keep learning Symfony! (with SymfonyCasts <3) ;-)

Cheers!

1 | Reply |
Peter L. avatar Peter L. 3 years ago

Hi all,
If someone is using his own Docker with this tutorial zipped code, is best to use version of PHP 7.2.
I haven't tried 7.1, 7.3, but in 7.4 templates are behaving weird. Like select element is merged with id `<selectid=""></selectid="">`
same for textarea and for top nav bar.
I think there must be some strange difference of processing whitespaces on PHP level.
Unrelated to Symfony but probably same bug: https://github.com/PrestaSh...

I was also required to generate thumbnails with Liip with command line in docker as browser links were not working. For example:
`php bin/console liip:imagine:cache:resolve article_image/asteroid-{hash}.jpeg`

| Reply |

Hey Peter L.!

Ah, I know that weird with the missing spaces! A certain, older version of Twig had a bug with whitespaces in PHP 7.4 - https://github.com/twigphp/... - it was fixed a long time ago, but this tutorial is also getting a little old. So that explains why PHP 7.4 suddenly acted poorly for you :). But since we advertise that this tutorial supports PHP 7.1-7.4, we'll get that fixed in the code download.

I'm not sure about the 2nd issue with why you would need to generating the thumbnails with the command... but, in case someone else has that issue, thanks for posting.

Cheers!

| Reply |

Thanks for letting me know about that bug. With Liip bundle, it may be my folder permissions in Docker.

| Reply |

I was just running around my old comments and noticed I resolved this problem while ago. It was not only about permissions. My docker nginx proxy was not passing proper Host header to underlaying nginx-phpfpm server. Liip is/was using this information to generate cached img urls.

| Reply |

Hey Peter,

Interesting! Thank you for sharing the solution with others!

Cheers!

| Reply |

Hi,
Thanks again for all thoses skills you share with us and for this amazing bundle.
I would like to know how to work with Tailwind CSS 2.1 with the JIT (Just In Time) compiler.
I manage to Maje or work. But I have to restart webpack to apply m'y changes.
Have a good day

| Reply |

Hey Moulaye,

Thank you for your interest in SymfonyCasts tutorials! I will add this Tailwind CSS to our idea pool for future screencasts. Unfortunately, I don't have estimations when it might be released if ever. Btw, Symfony should support Tailwind CSS since 5.x version, you can see the related PR been merged: https://github.com/symfony/...

Unfortunately, I personally can't say more about this topic as I know it very little and haven't tried to use yet. We will see if we can cover this topic in future tutorials.

Cheers!

| Reply |

Thank you,

Looking forward for a tailwind topic :)

Cheers

| Reply |

Hey!

What's the purpose of the second parameter in encore_entry_link_tags()? It looks like it tries to use packages from assets. If I use multiple webpack configs, and I've configured webpack_encore.builds in webpack_encore.yaml — it's clear, encore_entry_link_tags() reads corresponding entrypoints.json. But it also tries to read wrong manifest.json file specified by default in assets.yaml. It's very complicated. Builds... Packages... I'm lost. When I configure my Encore, I get only builds, and they contain both (entrypoints and manifest) JSON files, and it looks like one thing to me. But why are there two different parameters in encore_entry_link_tags()?

| Reply |

Hey plashenkov !

Yea, that second parameter is super unuseful. I added it to cover an edge case... but I'm not sure that edge case is even needed. So, to simplify, I would basically ignore that 2nd argument - pass null to it. So basically, you if you've configured multiple builds (it sounds like you have), then you have multiple builds... and that's it :).

That's sort of a non-answer, but it's (honestly) probably not that interesting to get into the real detail of these "packages" things. They are part of symfony/asset and aren't particularly useful when you're processing everything through Encore. It holds the 2nd argument because I coded for that edge-case first... and then later someone added the "multiple builds" feature to Encore.

Cheers!

| Reply |

Hey Ryan,
Thank you for your answer!
Yes, it looks like I've got it already. If you use Encore, you do not need to think about assets and packages at all. It should not use manifest.json, because it will use entrypoints.json. The problem is encore_entry_link_tags() tries to load default manifest.json specified in Symfony config (and it is specified there by default), and we get an exception. Even if null is passed as the second parameter. And we have to find out what's wrong, find it in config and disable. I've expressed all my first confusion here: https://github.com/symfony/...
Now it looks more or less clear to me, but I still think that something is super complicated with all these parameters. :)

| Reply |

Hey plashenkov!

Ah, I see that! Yea, the manifest.json file is only needed if you need to refer to built assets from a Twig file - most probably an image that used Encore to copy into the build directory - e.g. &lt;img src={{ asset('images/foo.png') }} - in that case Symfony will look for "images/foo.png" in the manifest.json file (assuming you have this configured in assets.yaml). But it's not really needed otherwise (it WAS used in an earlier version of WebpackEncoreBundle, but it's really not important now). In a perfect world, we maybe shouldn't concern ourselves at all with "asset packages" when we use the encore_entry functions. And as you mentioned in your issue, yes, "asset packages" and "builds" are completely different things... and you likely don't need multiple asset packages.

So yea, that 2nd arg about the asset packages is a little "not smooth" ;), but I hope this helps. We could maybe considering removing it in a future version, but that's tough to do without breaking backwards compatibility.

Cheers!

| Reply |

this seems to be a fun and likeable skill to learn, but what about our old "asset" way of handling simple assets? is it discontinued, or added into the core of symfony? to call images, css, javascripts, etc?

| Reply |

Hey jlchafardet

Using Webpack Encore is the prefered way of Symfony for handling assets but I believe you can use any other mechanism that fit your needs. Symfony does not obligates your to use Encore :)

Cheers!

1 | Reply |

I'm fairly clear about that, I just tested using the old asset method and worked fine, for now I'm just playing with some things I wanted to learn to do, so decided to not explore something else new while I finish learning those relations things from doctrine which I want to learn first, once I have mastered that,its very likely ill explore encore.

1 | Reply |

I highly recommend you to use Encore in your Symfony projects, it just make things a lot easier :)

1 | Reply |
Paul Z. avatar Paul Z. 4 years ago edited

I wasn't sure where to ask this question, I didn't want to add it as a encore github issue because it's probably not an issue but just me.

What i'm trying to use is encore, typescript, react, and linting typescript with using the '@typescript-eslint/parser' parser (since tslint is being deprecated). I got it all working, but my question is why do I still need the 'babel-eslint' package if i'm not using the babel eslint parser? Encore complains that I need it if I enable 'enableEslintLoader'. Here is the relevant parts of my webpack.config.js:


    .enableTypeScriptLoader()
    .enableForkedTypeScriptTypesChecking()
    .enableEslintLoader(options => {
        options.parser = '@typescript-eslint/parser'
    })
    .configureLoaderRule('eslint', loader => {
        loader.test = /\.(jsx?|tsx?)$/
    })
    .enableReactPreset()

Here is the relevant parts of my .eslintrc.js file:


    parser: "@typescript-eslint/parser",
    plugins: [
        "@typescript-eslint",
        "react"
    ],
    extends: [
        "eslint:recommended",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:react/recommended"
    ],
    parserOptions: {
        ecmaVersion: 2018,
        sourceType: 'module',
        ecmaFeatures: {
            jsx: true
        }
    }

Oh also the 'fork-ts-checker-webpack-plugin' now supports eslint, however when I enable it with:


    .enableForkedTypeScriptTypesChecking(tsConfig => {
        tsConfig.eslint = true;
    })

it doesn't seem to do anything. So i'm just wondering what I'm missing, or what's the best way to config eslint & typescript parser. Thanks in adance, your videos for react and api-platform has given me a huge head start in building my first headless app!

| Reply |

Hey Dave,

I don't know the exact reason but probably could give you some tips. First of all, JS dependencies are weird :) Really, when you run "yarn install" tons of tons JS packages are pulled locally. So, some dependencies still might require it. Also, you can give some hints from Yarn on it, try to execute "yarn why babel-eslint" to know why exactly it was installed. Does the output helps? Let me know if it's not. Btw, what exactly error message do you see when you don't have "babel-eslint" package installed?

Btw, I also noticed "babel-eslint" as a dependency in package.json of Webpack Encore, see https://github.com/symfony/... . So, it looks like a required dev dependency of Webpack Encore

Also, probably this issue could answers some questions for you? https://github.com/symfony/... . Otherwise, probably better to ask this question on webpack encore repo, probably someone there could answer this question better than me.

I hope this helps!

P.S. Thank you for your kind feedback about our courses! And congrats on your first headless app, well done!

Cheers!

| Reply |
Paul Z. avatar Paul Z. Victor 4 years ago edited

Hi Victor,

<br />/var/www/api-platform # yarn why babel-eslint<br />yarn why v1.16.0<br />[1/4] Why do we have the module "babel-eslint"...?<br />[2/4] Initialising dependency graph...<br />[3/4] Finding dependency...<br />[4/4] Calculating file sizes...<br />=> Found "babel-eslint@10.0.3"<br />info Has been hoisted to "babel-eslint"<br />info This module exists because it's specified in "devDependencies".<br />info Disk size without dependencies: "2.4MB"<br />info Disk size with unique dependencies: "6.18MB"<br />info Disk size with transitive dependencies: "13.52MB"<br />info Number of shared dependencies: 27<br />Done in 0.79s.<br />
so it's only a dependency because I added it. If I don't add it:
`
Running webpack-dev-server ...

WARNING Be careful when using Encore.configureLoaderRule(), this is a low-level method that can potentially break Encore and Webpack when not used carefully.
Error: Install eslint-loader & babel-eslint to use enableEslintLoader()

yarn add eslint-loader@^2.1.2 babel-eslint@^10.0.1 --dev

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
`

So it looks like it's a required dependency by webpack as it's the default parser even though it may not be used. I'll post the question on their github wiki. Thanks!

| Reply |

Hey Dave,

Hm, I see... For me it sounds like "eslint-loader & babel-eslint" are optional dependencies, they are required only when you use configureLoaderRule(). This way Encore does not want to force everyone pulling those packages as some customers might just not needed them. So maybe it's only listed in dev deps in package.json for clarity, or maybe it's used in some tests also.

Anyway, good idea to open an issue I think. Probably someone will be able to explain how it works with babel-eslint behind the scene better than I did.

Cheers!

| Reply |
Jordan avatar Jordan 4 years ago

How can I configure Foundation ?
I don't understand where is the problem ?

| Reply |

Hey Xidoc,

Could you explain more what exactly problem do you have? Any error messages that would help to figure out your problem? What exactly you want to configure? Did you follow any installation guide or docs? We would be glad to help you knowing a little more about your problem

Cheers!

| Reply |
Default user avatar Shamshid 5 years ago

great tutorial.

You should bring usage of purgecss in Encore which will trim out unwanted css and most importantly we need to learn to make a config to which trims page specific css from dedicated css file. (kinda confusing what I told here)

| Reply |

Hey Shamshid!

That's an excellent suggestion - I haven't played much with purge, but I've heard of it. I think getting it to work nicely requires (as you guessed) a bit of work... and I'm not sure it's worth it. I know Javier played with it for a bit, but ultimately decided not to use it. Here are some resources: https://github.com/symfony/... and https://github.com/symfony/...

Cheers!

| Reply |

Delete comment?

Share this comment

astronaut with balloons in space

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