Monday, July 27, 2015

How to Use Bootstrap (CSS only) and Webpack

I thought it was going to be easy.

require("bootstrap-webpack");

I thought that with a single line of JavaScript, I could have Bootstrap available for use within my web application. However, that was not to be the case. After battling through Webpack producing errors about CSS, woff2 and various other issues, I finally reached my limit at:

Uncaught ReferenceError: jQuery is not defined

I'm sure I'm not the first person to run into this issue (a quick Google produced this solution) but what pushed me over the edge was that I only wanted Bootstrap for it's CSS; I had no need to add jQuery as I wasn't going to use any of Bootstrap's JavaScriptiness. As far as I was concerned, this was another hoop and I wasn't going to jump through it.

With invigorated motivation and a fierce sense of purpose, I set out to get Webpack running with Bootstrap's CSS and nothing more. There had to be a way where I could load bootstrap.min.css and that is what I sought out. 


From my previous attempts to use bootstrap-webpack, I felt that if I had the correct Webpack loaders installed and configured correctly, Webpack should be able to load the CSS file. After playing around with adding and removing loaders, I settled on five that appear to be the bear minimum you need:
  • babel-loader: to transpile the "require" keyword
  • css-loader & style-loader: for processing CSS
  • file-loader: for handling "eot" resources
  • url-loader: for handling woff, woff2, ttf & svg resources

I expect that as Bootstrap evolves, this set of loaders will change but as of v3.3.5, these are all you need.

webpack.config.js:

module.exports = {
    entry: {
        app: ["webpack/hot/dev-server", "./app/app.js"],
    },
    output: {    
        path: './app',
        filename: 'bundle.js'
    },
    module: {
      loaders: [
            { test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel' },
            { test: /\.css$/, loader: 'style-loader!css-loader' },
            { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
            { test: /\.(woff|woff2)$/, loader:"url?prefix=font/&limit=5000" },
            { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
            { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" }
      ]
    }
};

The loaders were configured as above. Most of this was extracted from the configuration for bootstrap-webpack. The only change of note was updating the test for the url-loader to match both woff and woff2.

This solution is perfect for use with an Angular application where you are using angular.ui. Adding Bootstrap's JavaScript and jQuery in this scenario would be a waste as angular.ui provides the same set of features. 


Oh, and with the Babel-loader installed, you are free to use ES6 to your heart's content!

Feel free to check out the repo on GitHub

//Code, rinse, repeat

21 comments:

  1. Replies
    1. You're welcome! I'm glad you found it helpful.

      Delete
  2. Yay.. finally found how to do it. Thanks.

    ReplyDelete
  3. Yay.. finally found how to do it. Thanks.

    ReplyDelete
    Replies
    1. You're welcome! This seems to be catching quite a few people out.

      Delete
  4. Hi,
    I was very happy to find your solution.. I am using https://github.com/AngularClass/NG6-starter and I want to add 'Angular-ui-bootstrap' or 'angular-strap'.
    In the app.js of the project I included the import:
    import angularStrap from 'angular-strap';
    which seems to be working.

    But now I still need the CSS (as did you).

    So I installed bootstrap with NPM and in the app.js added:
    import 'bootstrap/dist/css/bootstrap.css';

    When I run the gulp command to build it spits out some errors like:
    ERROR in ./~/css-loader!./~/bootstrap/dist/css/bootstrap.css
    Module not found: Error: Cannot resolve module 'file' in /Users/mattijsspierings/www/prospa/NG6-starter/node_modules/bootstrap/dist/css
    @ ./~/css-loader!./~/bootstrap/dist/css/bootstrap.css 6:4445-4497 6:4520-4572

    ERROR in ./~/css-loader!./~/bootstrap/dist/css/bootstrap.css
    Module not found: Error: Cannot resolve module 'url' in /Users/mattijsspierings/www/prospa/NG6-starter/node_modules/bootstrap/dist/css
    @ ./~/css-loader!./~/bootstrap/dist/css/bootstrap.css 6:4622-4676

    It cant find module File or URL. Do I have to add these with NPM?
    I don't really understand how just adding your loaders would load the css of bootstrap. I am new to webpack.
    Do you have any idea what I am doing wrong?

    ReplyDelete
  5. okay. Kick me...I needed to add the dependency url-loader in my package.json. This was not documented, probably assumed by others. But the NG6-starter didnt have this depedency :)

    ReplyDelete
  6. Thanks! I'm baffled why this doesn't have a mainstream solution. I googled for a while even after finding this, but couldn't find a more idiomatic way. Thanks also for the repo for ultimate clarity.

    ReplyDelete
  7. Thanks, man. You saved me from banging my head against the wall with this one. My wall and I both thank you :).

    ReplyDelete
  8. Agree with previous posts - why is there not a mainstream solution for this? Tremendously useful MVP to install Bootstrap the right way. Thank you.

    ReplyDelete
  9. Worked like a charm - thanks so much for posting!

    ReplyDelete
  10. Any options here to use a custome config for bootstrap components ?

    ReplyDelete
  11. It works, but many CSS warning in the browser console, is it possible to get rid of?

    ReplyDelete
  12. Thanks! Works for bootswatch, too.

    Here's a slightly more comprehensive set of loader configurations that will work for font-awesome, as well:
    https://www.npmjs.com/package/font-awesome-webpack

    ReplyDelete
  13. Worked great for me! I also discovered how to get the JavaScript to work as well (e.g. how to expose jQuery globally):

    > Install the jquery package and the expose-loader package (both via NPM/Yarn)
    > Add the following line in the list of loaders in the webpack config:

    { test: /jquery\.js/, loader: 'expose-loader?jQuery' },

    This exposes the jQuery object in the global context so that Bootstrap can find it. Will eliminate the 'jQuery is not defined' error when parsing the bootstrap JavaScript file.

    Following Keith G's suggestion above, I have my loaders down to just the following 3:

    { test: /jquery\.js/, loader: 'expose-loader?jQuery' },
    { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
    loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
    { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file-loader' }

    ReplyDelete