Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Assets: Custom CSS and JS

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.

The EasyAdmin interface looks pretty great out of the box. But what if we want to customize the way something looks? For example, if I want to change the background on the sidebar. How can we do that?

This type of stuff can be controlled via the configureAssets() method. As a reminder, this is one of those methods that exists inside both our dashboard controller and each individual CRUD controller. So we can control assets on a global level or for just one section.

Let's make our change globally so that we can change the color of the sidebar on every page.

Hello configureAssets()

Anywhere inside of DashboardController, go back to the "Code"->"Generate..." menu, select "Override Methods" and override configureAssets():

... lines 1 - 10
use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
... lines 12 - 21
class DashboardController extends AbstractDashboardController
... lines 24 - 64
public function configureAssets(): Assets
return parent::configureAssets();

This has a lot of cool methods. There are some simple ones like ->addCssFile(). If you said ->addCssFile('foo.css'), that will include a link tag to /foo.css. As long as we have foo.css inside of our public/ directory, that would work.

The same thing goes for ->addJsFile(). And you can also ->addHtmlContentToBody() or ->addHtmlContentToHead(). There are tons of interesting methods!

Creating a Custom Admin Encore Entry

Our application uses Webpack Encore. Go check out the webpack.config.js file: it's pretty standard. We have just one entry called app:

... lines 1 - 8
... lines 10 - 16
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
.addEntry('app', './assets/app.js')
... lines 24 - 72
... lines 74 - 76

It's responsible for loading all of the JavaScript and CSS:

16 lines assets/app.js
* Welcome to your app's main JavaScript file!
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';
// start the Stimulus application
import './bootstrap';
// activates collapse functionality
import { Collapse } from 'bootstrap';

and we include this entry on our frontend to get everything looking and working well.

You probably noticed that, in configureAssets(), there's an addWebpackEncoreEntry() method. If we said app here, that would pull in the CSS and JavaScript from our app entry. But.... that makes things look a little crazy... because we do not want all of our frontend styles and JavaScript to show up in the admin section. Nope, we just want to be able to add a little bit of new stuff.

So here's what we'll do instead. Inside the assets/styles/ directory, create an entirely new file called admin.css. This will be our CSS solely for styling the admin section. And just to see if things are working, I'll add a very lovely body background of "lightcyan":

body {
background: lightcyan;


Over in webpack.config.js, add a second entry for just the admin. But, right now, since we only have a CSS file (we don't need JavaScript), I'll say .addStyleEntry()... and point it to ./assets/styles/admin.css. I should also change app to admin... but I'll catch that in a minute:

... lines 1 - 8
... lines 10 - 23
.addStyleEntry('admin', './assets/styles/admin.css')
... lines 25 - 73
... lines 75 - 77

Because we just modified our webpack file, we need to go over to our terminal, find where we're running encore, hit Ctrl+C, and then rerun it:

yarn watch

And... it exploded! That's from my mistake! I need to give my entry a unique name. Change app to admin:

... lines 1 - 8
... lines 10 - 23
.addStyleEntry('admin', './assets/styles/admin.css')
... lines 25 - 73
... lines 75 - 77

Run it again, and... beautiful!

In addition to the original stuff, you can see that it also dumped an admin.css file. Thanks to this, over in our DashboardController, say ->addWebpackEncoreEntry('admin'):

... lines 1 - 21
class DashboardController extends AbstractDashboardController
... lines 24 - 64
public function configureAssets(): Assets
return parent::configureAssets()

Refresh and... it works! That's a... well... interesting-looking page.

If you View the page source, you can see how this works. There's really nothing special. The app.css file gives us all of the EasyAdmin styling that we've been enjoying... and then here is our new admin.css file.

CSS Properties

At this point, we're dangerous! We can add whatever CSS we want to the new admin.css file and it will override any of the EasyAdmin styles. Cool! But EasyAdmin makes it even easier than that!

Inspect the element on the sidebar. The goal is to change the sidebar background. Find the actual element with the sidebar class. If you look over at the styles on the right... I'll make this a little bit bigger... you can see that the .sidebar class has a background style. But instead of it being set to a color, it's set to this var(--sidebar-bg) thing. If you hover over it, apparently, this is equal to #f8fafc.

If you haven't seen this before, this is a CSS property. It has nothing to do with EasyAdmin or Symfony. In CSS, you can create variables (called "CSS properties") and reference them somewhere else. EasyAdmin, apparently, created a --sidebar-bg variable and is referencing it here. So, instead of trying to override the background of .sidebar - which we could do - we can override this CSS property and it will have the same effect.

How? Let's cheat a little bit by digging deep into EasyAdmin itself.

Open vendor/easycorp/easyadmin-bundle/assets/css/easyadmin-theme/. Inside, there's a file called variables-theme.scss. This is where all of these CSS properties are defined. And there's tons of stuff here, for font sizes, different widths, and... --sidebar-bg! This --sidebar-bg variable, or property, is apparently set to another variable via the var syntax. You'll find that variable in another file called ./color-palette.scss... which is right here. These are SCSS files, but this CSS property system has nothing to do with Sass. This is a pure CSS feature.

There's a lot here, but if you follow the logic, --sidebar-bg is set to --gray-50... then all the way at the bottom, --gray-50 is set to --blue-gray-50... then that... if we keep looking... yes! It's set to the color we expected!

This is a great way to learn what these values are, how they relate to one another and how to override them. Copy the --sidebar-bg syntax.

The way you define CSS variables is typically under this :root pseudo-selector. We're going to do the same thing.

In our CSS file, remove the body, add :root and then paste. And while it's totally legal to reference CSS properties from here, let's replace that with a normal hex color:

:root { --sidebar-bg: #deebff; }

Let's try it! Watch the sidebar closely... the change is subtle. Refresh and... it changed! To prove it, if you find the --sidebar-bg on the styles and hover... that property is now set to #deebff. It's subtle, but it is loading the correct color!

So we just customized the assets globally for our entire admin section. But we could override configureAssets() in a specific CRUD controller to make changes that only apply to that section.

Next, let's start digging into what is quite possibly the most important part of configuring EasyAdmin: Fields. These control which fields show up on the index page, as well as the form pages.

Leave a comment!

Login or Register to join the conversation
Tomáš K. Avatar
Tomáš K. Avatar Tomáš K. | posted 1 year ago | edited

Seems like the --sidebar-bg value change didn't have any effect since the hex value shouldn't be in quotes :) It's totally unimportant, just if someone had trouble figuring it out.

4 Reply

Hey Tomáš K.

You're right! Thanks for sharing it with others

2 Reply

Hi, it seems that the only way to use easyadmin's assets, after extending its layout, is to do
# remove the --symlink option if your system doesn't support symbolic links
php bin/console assets:install --symlink
There is no way to use webpack encore.


Hey @pasquale_pellicani ,

That's a third-party bundle's code, so it's supposed to be used this way. Actually, EA uses Webpack Encore behind the scene, see https://github.com/EasyCorp/EasyAdminBundle/blob/4.x/webpack.config.js . But for simplicity, the EA pre-compiled all those assets and commit the compiled version to the repo so that users of this bundle even don't have to have Webpack Encore installed in their projects, it just works out of the box with the way you mentioned, which is also a best practice way for Symfony bundles.

But if you want to pass those files through your own Webpack Encore config - you can, in theory. I think you just need to add all those files to your config, compile them, and include them to the EA templates. I suppose you may want to override EA assets to avoid doubling those assets. But on practice I don't see a value in it unless you want to change something internally in the EA assets? But if you just need to add some behaviour - you don't have to touch EA assets code, just create a new Webpack Encore entry and include it to the specific crud controller, etc. with addWebpackEncoreEntry() method.

I hope this helps!


1 Reply

Hi, thanks for the reply, but if I add a new entry in my webpack-encore, which path should I link it to ?

so I shouldn't use the asset::install command.


Hey @pasquale_pellicani ,

I suppose the relative path to the source assets in your vendor/... directory :) pointing to the compiled assets in your Webpack Encore config file is not quite correct, as that's already a compiled version of the assets :) Basically, you need to point to the same files the EA Webpack Encore config points to.


1 Reply

..maybe this correct ?
(in webpack.config.js file)
.addEntry('easy-admin', './vendor/easycorp/easyadmin-bundle/assets/js/app.js')

after yarn --watch return this.... :'(

Module build failed: Module not found:
"./vendor/easycorp/easyadmin-bundle/assets/js/app.js" contains a reference to the file "bootstrap/dist/js/bootstrap.bundle".
This file can not be found, please check it for typos or update it if the file got moved.

"./vendor/easycorp/easyadmin-bundle/assets/js/app.js" contains a reference to the file "mark.js/src/vanilla".
This file can not be found, please check it for typos or update it if the file got moved.

"./vendor/easycorp/easyadmin-bundle/assets/js/autocomplete.js" contains a reference to the file "tom-select/dist/js/tom-select.complete.min".
This file can not be found, please check it for typos or update it if the file got moved.

Hey @pasquale_pellicani

It seems correct, and the error message says that it's processing that file... but could not find other files that it imports, e.g. bootstrap/dist/js/bootstrap.bundle file. Make sure those files exist. Though I'm not sure if you need to install Node deps in that ./vendor/easycorp/easyadmin-bundle/ or no :/ Most probably you should add those deps to your package.json file so that the Webpack Encore could get access to them, because the EA file you're trying to add depends on those.


1 Reply

Hi, thanks for the reply, but shouldn't it be the responsibility of the bundle to have an already working app.js file? And then if I modify my webpack.config.js I risk compromising the frontend assets (where I use a different template than EasyAdmin's) or not ?


Hey @pasquale_pellicani ,

Well, the responsibility of the bundle is to provide you an out-of-the-box working solution, and the bundle provides you with already compiled assets that you can just use. If you want to worry about compiling those assets yourself - that's more work on your side, but that's up to you I think.

Why do you think the app.js file does not work? It's normal that your custom JS code depends on more low level libraries, e.g. you depend on Bootstrap if you use their modals, you depend on Stimulus core files if you write custom Stimulus controllers. The EA JS code depends on some libraries you are missing in your project that's why you see those errors.

And then if I modify my webpack.config.js I risk compromising the frontend assets (where I use a different template than EasyAdmin's) or not ?

Not sure I understand. Do you mean you don't use Bootstrap but another frontend toolkit? If so, I suppose it depends if your dependencies conflict with it or no. As soon as you can add it to your package.json - it should be OK I think.... as long as you do not import those new libs to your personal JS files.

Please, read this docs, maybe you will find an answer how to do what you want without trying to compile EA assets manually: https://symfony.com/bundles/EasyAdminBundle/current/design.html



Hello! Once I try to run this commend "npm run watch"
I have this issue:
WARNING Webpack is already provided by Webpack Encore, also adding it to your package.json file may cause issues.

Did i do something wrong from my side?

Thansk a lot!


Hey Lubna,

I'm not sure... Do you use npm package manager instead of Yarn? Or did you just mislead the command? Because in this course we use Yarn, and the command should be "yarn watch" instead of "npm run watch". So, this might probably happen because npm sees that your project already uses Yarn, but I'm not sure.


Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", //
        "doctrine/doctrine-bundle": "^2.1", // 2.5.5
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.2.1
        "doctrine/orm": "^2.7", // 2.10.4
        "easycorp/easyadmin-bundle": "^4.0", // v4.0.2
        "handcraftedinthealps/goodby-csv": "^1.4", // 1.4.0
        "knplabs/knp-markdown-bundle": "dev-symfony6", // dev-symfony6
        "knplabs/knp-time-bundle": "^1.11", // 1.17.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.5
        "stof/doctrine-extensions-bundle": "^1.4", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.1
        "symfony/console": "6.0.*", // v6.0.2
        "symfony/dotenv": "6.0.*", // v6.0.2
        "symfony/flex": "^2.0.0", // v2.0.1
        "symfony/framework-bundle": "6.0.*", // v6.0.2
        "symfony/mime": "6.0.*", // v6.0.2
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.0
        "symfony/security-bundle": "6.0.*", // v6.0.2
        "symfony/stopwatch": "6.0.*", // v6.0.0
        "symfony/twig-bundle": "6.0.*", // v6.0.1
        "symfony/ux-chartjs": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.7", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.7
        "twig/twig": "^2.12|^3.0" // v3.3.7
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.1
        "symfony/debug-bundle": "6.0.*", // v6.0.2
        "symfony/maker-bundle": "^1.15", // v1.36.4
        "symfony/var-dumper": "6.0.*", // v6.0.2
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.2
        "zenstruck/foundry": "^1.1" // v1.16.0