How to Install Twitter Bootstrap into a Webpack/React.js Application

Installing Twitter Bootstrap into my React.js application took more effort that I would have liked. There were several problems I ran into.

Bootstrap v3.3.7 fails

Bootstrap 4 is only beta, but it looks like I'm going to have to use it. I had all sorts of problems getting the includes and dependencies to work with v3.3.7, such as this inability to resolve fonts:

ERROR in ./node_modules/css-loader!./node_modules/postcss-loader/lib?{}!./node_modules/sass-loader/lib/loader.js!./stylesheets/style.scss
Module not found: Error: Can't resolve '../fonts/bootstrap/glyphicons-halflings-regular.eot' in '/Users/wkotzan/Development/momo-scans-frontend/stylesheets'
 @ ./node_modules/css-loader!./node_modules/postcss-loader/lib?{}!./node_modules/sass-loader/lib/loader.js!./stylesheets/style.scss 7:4360-4422 7:4453-4515
 @ ./stylesheets/style.scss
 @ ./js/App.jsx
 @ multi (webpack)-dev-server/client?http://localhost:8080 ./js/App.jsx

I could probably troubleshoot some more, but not worth the effort given that Bootstrap 3.3.7 will probably be obsolete very soon. So onto v4.0.0....

jQuery problems

First, you need to get the Javascript dependencies within Bootstrap working. I have the following at the top of my App.jsx application file:

import 'bootstrap';

But then I started getting a browser error that the jQuery dependency could not be found and my application page wouldn't render. I had to fix this by doing a yarn add jquery and then adding the following to my Webpack config file:

const webpack = require('webpack');

...

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

This makes the JQuery references in Bootstrap universally available.

Getting the styles to work

For the styles to wor, I had to add this line to my master style.scss file:

@import "~bootstrap/scss/bootstrap";

And then I had to add the following processing plugins to my Webpack config file:

  module: {
    rules: [
      {
        test: /\.scss?$/,
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
          {
            loader: "postcss-loader",
            options: {
              plugins: function() {
                return [
                  require('precss'),
                  require('autoprefixer')
                ]
              }
            }
          },
          { loader: "sass-loader" }
        ]
      },
    ]
  },

Final webpack.config.js

Here's my complete Webpack config file:

const path = require('path');
const webpack = require('webpack');

module.exports = {
  context: __dirname,
  entry: "./js/App.jsx",
  watch: true,
  devtool: "cheap-eval-source-map",
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
    publicPath: '/dist/',
    historyApiFallback: true
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json']
  },
  stats: {
    colors: true,
    reasons: true
  },
  module: {
    rules: [
      {
        test: /\.scss?$/,
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
          {
            loader: "postcss-loader",
            options: {
              plugins: function() {
                return [
                  require('precss'),
                  require('autoprefixer')
                ]
              }
            }
          },
          { loader: "sass-loader" }
        ]
      },
      { test: /\.jsx?$/, use: 'babel-loader' }
    ]
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
}

Information source: TWBS documentation, Stack Overflow