Using Bootstrap in a ES6 Webpack Application

From my post history you can tell that I spend most of my time working on backend or non-user facing applications, but occasionally I make my way back to the frontend in personal projects or otherwise. Lately I’ve been playing with React, webpack and a bunch of other tools that fall into the ecosystem.

One of the things that I was really struggling with initially though was how to actually get webpack to load non-javascript resources into the browser. Specifically this came up as I wanted to add Bootstrap to my React application. If you’re also struggling with this, read on below or feel free to jump to the bottom to see all of the code.

I’m assuming you already have a basic ES6 application with webpack setup and running. If so, you have an index.html being served up with an index.jsx that’s being compiled and your webpack.config.js looks something like this:

var webpack = require('webpack')
var path = require('path')

module.exports = {
    entry: [
        './src/index.jsx'
    ],
    resolve: {
        extensions: ['', '.js', '.jsx',]
    },
    devtool: 'cheap-source-map',
    output: {
        path: __dirname,
        filename: 'bundle.js',
        publicPath: '/static/'
    },
    module: {
        loaders: [
            { test: /\.jsx/, loader: 'babel', include: path.join(__dirname, 'src') }
        ]
    }
}

Following the walkthrough below will take you to a point where webpack will have loaded the bootstrap CSS and JS into your page. If you’d like to skip to the end, feel free, but if you run into any issues it might be worth skimming through it to make sure you understand what’s going on.

Getting the Bootstrap JS code loaded

The first thing you’re going to want to do is include the Bootstrap JS code. Let’s “npm install bootstrap”. Then let’s import the bootstrap code into our code so webpack knows we depend on it. This can be done fairly trivially by doing the following at the top of your index.jsx:

import 'bootstrap'

Once you save the page, you should see webpack pick up the change and if you look at the bundle messages, you’ll see that the bootstrap javascript files were loaded. That was easy!

Then, upon reloading your index.html, you’ll notice that nothing shows up and there’s an error in the console:

Uncaught ReferenceError: jQuery is not defined

This is because Bootstrap’s js code depends on jQuery and webpack hasn’t exposed the jQuery variable. First, “npm install jquery”. Then, to allow it to export the name, we need to use webpack’s dependency injection using ProvidePlugin. To do this, just add a plugin section in your webpack.config.js that looks like the following:

    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery'
        })  
    ]  

Restart your server so that it picks up your webpack config changes and you’ll see that the jQuery error is gone. Now we can proceed to loading the CSS.

Loading the CSS and associated files

Now we want to make sure the CSS files get loaded. Once again we can do this relatively simply with another import in our index.jsx file:

import 'bootstrap/dist/css/bootstrap.css'

It’s pretty weird to me that we’re importing a CSS file into our jsx code but it just works and I’ve been told this is the way it should be done.

If you save that file, you’ll probably see a new error from webpack:

ERROR in ./~/bootstrap/dist/css/bootstrap.css
Module parse failed: ...project_dir.../node_modules/bootstrap/dist/css/bootstrap.css Unexpected token (7:5)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (7:5)

This makes sense. We haven’t told webpack how to load CSS files. So to do this we need to install the required loaders:

npm install css-loader file-loader url-loader style-loader

Then we can add the following element to our loaders section:

        loaders: [
            ...
            { test: /\.css$/, loader: 'style!css' },
            ...
        ]

Reloading the server will give us a new error:

ERROR in ./~/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
Module parse failed: ...project_dir.../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 Unexpected character '' (1:4)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '' (1:4)
...
ERROR in ./~/bootstrap/dist/fonts/glyphicons-halflings-regular.eot
Module parse failed: ...project_dir.../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.eot Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '�' (1:0)
...
ERROR in ./~/bootstrap/dist/fonts/glyphicons-halflings-regular.woff
Module parse failed: ...project_dir...s/node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff Unexpected character '' (1:4)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '' (1:4)
...
ERROR in ./~/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf
Module parse failed: ...project_dir.../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '' (1:0)

It turns out the CSS file references a bunch of other files, such as svg files, and fonts (woff, woff2, tff, eot). To deal with this we’ll add an additional loader element:

        loaders: [
            ...
            { test: /\.(svg|ttf|woff|woff2|eot)$/, loader: 'url?limit=5000' },
            ...
        ]

This is telling webpack to use the url-loader for files 5000 bytes, it will instead fall back to the file loader.

Everything combined

By the end, your webpack.config.js should look something like this:

var webpack = require('webpack')
var path = require('path')

module.exports = {
    entry: [
        './src/index.jsx'
    ],
    resolve: {
        extensions: ['', '.js', '.jsx',]
    },
    devtool: 'cheap-source-map',
    output: {
        path: __dirname,
        filename: 'bundle.js',
        publicPath: '/static/'
    },
    module: {
        loaders: [
            { test: /\.jsx/, loader: 'babel', include: path.join(__dirname, 'src') },
            { test: /\.css$/, loader: 'style!css' },
            { test: /\.(svg|ttf|woff|woff2|eot)$/, loader: 'url?limit=5000' },
        ]
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery'
        })
    ]
}

And you should have had to installed a few npm modules:

npm install bootstrap
npm install jquery
npm install css-loader file-loader url-loader style-loader

And you should have had to add two imports to your index.jsx file:

import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.css'

Leave a Reply

Your email address will not be published. Required fields are marked *