Encore: Setting up your Project

5.4 version
Symfony 5.4 is backed by Private Packagist.

Encore: Setting up your Project

After installing Encore, your app already has one CSS and one JS file, organized into an assets/ directory:

  • assets/app.js
  • assets/styles/app.css

With Encore, think of your app.js file like a standalone JavaScript application: it will require all of the dependencies it needs (e.g. jQuery or React), including any CSS. Your app.js file is already doing this with a JavaScript import statement:

1
2
3
4
// assets/app.js
// ...

import './styles/app.css';

Encore’s job (via Webpack) is simple: to read and follow all of the require() statements and create one final app.js (and app.css) that contains everything your app needs. Encore can do a lot more: minify files, pre-process Sass/LESS, support React, Vue.js, etc.

Configuring Encore/Webpack

Everything in Encore is configured via a webpack.config.js file at the root of your project. It already holds the basic config you need:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// webpack.config.js
const Encore = require('@symfony/webpack-encore');

Encore
    // directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // public path used by the web server to access the output path
    .setPublicPath('/build')

    .addEntry('app', './assets/app.js')

    // ...
;

// ...

The key part is addEntry(): this tells Encore to load the assets/app.js file and follow all of the require() statements. It will then package everything together and - thanks to the first app argument - output final app.js and app.css files into the public/build directory.

To build the assets, run the following if you use the Yarn package manager:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# compile assets once
$ yarn encore dev

# or, recompile assets automatically when files change
$ yarn encore dev --watch

# on deploy, create a production build
$ yarn encore production

# if you use the npm package manager
$ npm install react react-dom prop-types --save

If you use the npm package manager, run the following commands instead:

1
2
3
4
5
6
7
8
# compile assets once
$ npm run dev

# or, recompile assets automatically when files change
$ npm run watch

# on deploy, create a production build
$ npm run build

Note

Stop and restart encore each time you update your webpack.config.js file.

Congrats! You now have three new files:

  • public/build/app.js (holds all the JavaScript for your “app” entry)
  • public/build/app.css (holds all the CSS for your “app” entry)
  • public/build/runtime.js (a file that helps Webpack do its job)

Next, include these in your base layout file. Two Twig helpers from WebpackEncoreBundle can do most of the work for you:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <!-- ... -->

        {% block stylesheets %}
            {# 'app' must match the first argument to addEntry() in webpack.config.js #}
            {{ encore_entry_link_tags('app') }}

            <!-- Renders a link tag (if your module requires any CSS)
                 <link rel="stylesheet" href="/build/app.css"> -->
        {% endblock %}

        {% block javascripts %}
            {{ encore_entry_script_tags('app') }}

            <!-- Renders app.js & a webpack runtime.js file
                <script src="/build/runtime.js" defer></script>
                <script src="/build/app.js" defer></script>
                See note below about the "defer" attribute -->
        {% endblock %}
    </head>

    <!-- ... -->
</html>

That’s it! When you refresh your page, all of the JavaScript from assets/app.js - as well as any other JavaScript files it included - will be executed. All the CSS files that were required will also be displayed.

The encore_entry_link_tags() and encore_entry_script_tags() functions read from an entrypoints.json file that’s generated by Encore to know the exact filename(s) to render. This file is especially useful because you can enable versioning or point assets to a CDN without making any changes to your template: the paths in entrypoints.json will always be the final, correct paths. And if you use splitEntryChunks() (where Webpack splits the output into even more files), all the necessary script and link tags will render automatically.

If you’re not using Symfony, you can ignore the entrypoints.json file and point to the final, built file directly. entrypoints.json is only required for some optional features.

New in version 1.9.0: The defer attribute on the script tags delays the execution of the JavaScript until the page loads (similar to putting the script at the bottom of the page). The ability to always add this attribute was introduced in WebpackEncoreBundle 1.9.0 and is automatically enabled in that bundle’s recipe in the config/packages/webpack_encore.yaml file. See WebpackEncoreBundle Configuration for more details.

Requiring JavaScript Modules

Webpack is a module bundler, which means that you can import other JavaScript files. First, create a file that exports a function:

1
2
3
4
// assets/greet.js
export default function(name) {
    return `Yo yo ${name} - welcome to Encore!`;
};

We’ll use jQuery to print this message on the page. Install it via:

1
2
3
4
5
# if you use the Yarn package manager
$ yarn add jquery --dev

# if you use the npm package manager
$ npm install jquery --save-dev

Great! Use import to import jquery and greet.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  // assets/app.js
  // ...

+ // loads the jquery package from node_modules
+ import jquery from 'jquery';

+ // import the function from greet.js (the .js extension is optional)
+ // ./ (or ../) means to look for a local file
+ import greet from './greet';

+ $(document).ready(function() {
+     $('body').prepend('<h1>'+greet('jill')+'</h1>');
+ });

That’s it! If you previously ran encore dev --watch, your final, built files have already been updated: jQuery and greet.js have been automatically added to the output file (app.js). Refresh to see the message!

Page-Specific JavaScript or CSS (Multiple Entries)

So far, you only have one final JavaScript file: app.js. For small applications or SPA’s (Single Page Applications), that might be fine! However, as your app grows, you may want to have page-specific JavaScript or CSS (e.g. checkout, account, etc.). To handle this, create a new “entry” JavaScript file for each page:

1
2
// assets/checkout.js
// custom code for your checkout page
1
2
// assets/account.js
// custom code for your account page

Next, use addEntry() to tell Webpack to read these two new files when it builds:

1
2
3
4
5
6
7
  // webpack.config.js
  Encore
      // ...
      .addEntry('app', './assets/app.js')
+     .addEntry('checkout', './assets/checkout.js')
+     .addEntry('account', './assets/account.js')
      // ...

And because you just changed the webpack.config.js file, make sure to stop and restart Encore:

1
2
3
4
5
# if you use the Yarn package manager
$ yarn run encore dev --watch

# if you use the npm package manager
$ npm run watch

Webpack will now output a new checkout.js file and a new account.js file in your build directory. And, if any of those files require/import CSS, Webpack will also output checkout.css and account.css files.

Finally, include the script and link tags on the individual pages where you need them:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  {# templates/.../checkout.html.twig #}
  {% extends 'base.html.twig' %}

+ {% block stylesheets %}
+     {{ parent() }}
+     {{ encore_entry_link_tags('checkout') }}
+ {% endblock %}

+ {% block javascripts %}
+     {{ parent() }}
+     {{ encore_entry_script_tags('checkout') }}
+ {% endblock %}

Now, the checkout page will contain all the JavaScript and CSS for the app entry (because this is included in base.html.twig and there is the {{ parent() }} call) and your checkout entry.

See Creating Page-Specific CSS/JS for more details. To avoid duplicating the same code in different entry files, see Preventing Duplication by “Splitting” Shared Code into Separate Files.

Using Sass/LESS/Stylus

You’ve already mastered the basics of Encore. Nice! But, there are many more features that you can opt into if you need them. For example, instead of using plain CSS you can also use Sass, LESS or Stylus. To use Sass, rename the app.css file to app.scss and update the import statement:

1
2
3
  // assets/app.js
- import './styles/app.css';
+ import './styles/app.scss';

Then, tell Encore to enable the Sass pre-processor:

1
2
3
4
5
6
  // webpack.config.js
  Encore
      // ...

+    .enableSassLoader()
  ;

Because you just changed your webpack.config.js file, you’ll need to restart Encore. When you do, you’ll see an error!

1
2
>   Error: Install sass-loader & sass to use enableSassLoader()
>     yarn add [email protected]^10.0.0 sass --dev

Encore supports many features. But, instead of forcing all of them on you, when you need a feature, Encore will tell you what you need to install. Run:

1
2
3
4
5
6
7
# if you use the Yarn package manager
$ yarn add [email protected]^10.0.0 sass --dev
$ yarn encore dev --watch

# if you use the npm package manager
$ npm install [email protected]^10.0.0 sass --save-dev
$ npm run watch

Your app now supports Sass. Encore also supports LESS and Stylus. See CSS Preprocessors: Sass, LESS, Stylus, etc..

Compiling Only a CSS File

Caution

Using addStyleEntry() is supported, but not recommended. A better option is to follow the pattern above: use addEntry() to point to a JavaScript file, then require the CSS needed from inside of that.

If you want to only compile a CSS file, that’s possible via addStyleEntry():

1
2
3
4
5
6
// webpack.config.js
Encore
    // ...

    .addStyleEntry('some_page', './assets/styles/some_page.css')
;

This will output a new some_page.css.

Keep Going!

Encore supports many more features! For a full list of what you can do, see Encore’s index.js file. Or, go back to list of Encore articles.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.