Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Bower Components out of web

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

Great news! We've minified and combined all our CSS into just one file. Oh, except for the Bootstrap CSS! We have two good options. First, we could point the link to a public Bootstrap CDN. Or we could cram the bootstrap styling right into main.css. Let's do that - it's a lot more interesting.


Bootstrap is installed thanks to Bowser. Um, I mean bower. Bower is the Composer for frontend assets, like Bootstrap or jQuery. It's an alternative to downloading the files and committing them to your repo.

Bower reads from, surprise!, a bower.json file:

20 lines bower.json
"name": "knpu-gulp",
... lines 3 - 15
"dependencies": {
"bootstrap": "~3.3.2"

And when I created the project, I also added a .bowerrc file. This told bower to download things into this web/vendor directory:

4 lines .bowerrc
"directory": "web/vendor"

That made them publicly accessible.

But now that we have the muscle of Gulp, I want to complicate things. Change this to be vendor/bower_components:

4 lines .bowerrc
"directory": "vendor/bower_components"

That'll put these files outside of the public directory, which at first, will cause some issues. Delete that old stuff:

rm -rf web/vendor

Head to the terminal and ctrl+c out of gulp. Now, run bower install:

bower install

If you don't have the bower command, just check out their docs. It's installed globally via npm, exactly like gulp.

Done! Now, in my vendor/ directory I have a beautiful bower_components folder.

Adding CSS Files to our Gulp Styles Stream

But even if I wanted to have 2 separate link tags in my layout, I can't: Bootstrap no longer calls the web/ directory home. So, get rid of its link tag.

In gulpfile.js, let's try to fix things! I'll start by adding a new configuration variable called bowerDir, because it's going to be really common to refer to things in that directory. Set it to vendor/bower_components:

41 lines gulpfile.js
... lines 1 - 3
var config = {
... lines 5 - 8
bowerDir: 'vendor/bower_components'
... lines 11 - 41

If you open that directory, you can see where the bootstrap.css file lives. Notice, it's not a Sass file - just regular old CSS. There actually is a Sass version of Bootstrap, and you can totally use this instead if you want to control your Bootstrap variables.

But the question is, can we push plain CSS files through our Sass-processing addStyle function? Sure! Let's add config.bowerDir then /bootstrap/dist/css/bootstrap.css:

41 lines gulpfile.js
... lines 1 - 23
gulp.task('sass', function() {
], 'main.css');
... lines 30 - 33
... lines 35 - 41

And we don't even need to worry about getting the min file, because we're already taking care of that. This file will go through the sass filter. But that's ok! It'll just look like the most boring Sass file ever.

Head back and run gulp:


And now, main.css starts out with glorious Bootstrap code. And it would be minified if I had passed the --production flag.

Our site should still look great. So refresh. Yep, it's just like before, but with one less pesky CSS file to download.

Renaming the Task to styles

And before we keep going, I think we can make another improvement. Our task is called sass. Let's change this to styles, because it's job is really to process styles, whether those are CSS or Sass files.

41 lines gulpfile.js
... lines 1 - 23
gulp.task('styles', function() {
... lines 25 - 33
... lines 35 - 41

We also need to change that name on the default task. Ooops, and before restarting Gulp, also change the name on the watch task to styles:

41 lines gulpfile.js
... lines 1 - 35
gulp.task('watch', function() {
gulp.watch(config.assetsDir+'/'+config.sassPattern, ['styles'])
... line 39
gulp.task('default', ['styles', 'watch']);

Ok, now we can try things. ctrl+c to stop Gulp, and re-start it:


Yes, no errors! And the site still looks Tri-tastic! See what I did there?

Leave a comment!

Login or Register to join the conversation
Default user avatar
Default user avatar Алина Туранова | posted 3 years ago | edited

Firts of all, thanks to the author of the entire Gulp course!) It was exactly that I was looking for) <b><3</b>

After many tries to avoid errors such as
<i>Cannot read property 'denominator' of undefined in file .../bootstrap/dist/css/bootstrap.css line no.</i>

For those who have some difficulties with merging sass/less and css files - as alternative way you can just install <i>bootstrap (jquery, popper and else) with npm</i>, then use <i>merge2</i> to concat everything :)

<b>my versions:</b>
npm 6.14.2

 CLI version: 2.2.0
 Local version: 4.0.2

bootstrap 4.4.1


const gulp = require('gulp');
const cleanCSS = require('gulp-clean-css');
const merge = require('merge2');
const environments = require('gulp-environments');
const plugins = require('gulp-load-plugins')();

let dev = environments.development;
let prod = environments.production;

gulp.task('styles', function () {
    var cssFiles = gulp.src(["node_modules/bootstrap/dist/css/bootstrap.css"])

    var lessCssFiles = gulp.src(config.assetsPath + config.lessPattern)
        // Plumber prevents gulp from throwing a proper error exit code. When building for production, you may want a proper error.
        .pipe(dev(plugins.plumber(function(error) {

    return merge(cssFiles, lessCssFiles)

Hey Alina,

Thank you for this tip! :) And thanks for kind words about this course. Btw, in case you're interested in Frontend stuff - we have a few more courses for you:

We used Gulf before for SymfonyCasts website assets, but then switched to Webpack Encore :)


Default user avatar
Default user avatar Алина Туранова | Victor | posted 3 years ago

Victor, thank you for the recommendations! I'll definitely use it in future)
So pity that I didn't find that Webpack Encore course earlier(( I started to use it for my project but found that it wasn't understood as easy as Gulp for me. As I'm a backend developer on 80-90%, I decided just to use Gulp this time in the name of speed =)


Hey Alina,

Ah, I see :) Yes, Gulp is a great tool for the purposes it's designed too. But Webpack is just another level, and yes, Webpack Encore was designed right for backend developers ;) I hope after our course it should be pretty clear for you! If not- just ask questions in comments below a video you didn't get ;)



After add the instruction config.bowerDir+'/bootstrap/dist/css/bootstrap.css' I have an error when I start gulp. In the terminal, there is this error : Error: ENOENT: no such file or directory, open '/home/stephane/www/gulp/app/Resources/assets/sass/bootstrap.css.map'
You know how I can correct this ? Thank.


Hey Stéphane!

Hmm, I'm not sure about this - I can't repeat it locally. However, it's very likely that some change to some newer version of a library (probably sourcemaps related) is causing this issue. If you turn sourcemaps off, does the issue go away? Does everything work if you point to the bootstrap.css file that lives in the web directory (i.e. before the steps in this chapter)?


Default user avatar
Default user avatar Paul-André Duchesne | Stephane | posted 5 years ago

Hello Stéphane,

I've been having the same problem you've got. While looking at the error stack trace, it was mentioning it's coming from clean-css plugin.
I guess that, like me, you followed the suggestion of not using gulp-minify-css plugin because of deprecation and to use gulp-clean-css instead...
The only workaround I've currently found was to produce two different minified css: one for everything's coming from vendor/bower_components (bootstrap en fontawesome) that I named vendor.css and one for everything's coming from app/Resources/assets that I named main.css:
gulp.task('styles', function() {
var pipeline = new Pipeline();

], 'vendor.css');

], 'main.css');

return pipeline.run(app.addStyle);

It's not the ideal solution, but at least it's not failing anymore :)

Kind regards,



Thanks for sharing Paul-André - and sorry about the issue! There is definitely some problem here - but I also haven't been able to nail it down yet.


Hi Paul-André,
Thank you for your solution.

Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.6.*", // v2.6.4
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.6
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.2.0
        "symfony/assetic-bundle": "~2.3", // v2.5.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.7
        "symfony/monolog-bundle": "~2.4", // v2.6.1
        "sensio/distribution-bundle": "~3.0", // v3.0.9
        "sensio/framework-extra-bundle": "~3.0", // v3.0.3
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "hautelook/alice-bundle": "~0.2" // 0.2
    "require-dev": {
        "sensio/generator-bundle": "~2.3" // v2.4.0