Preferred way of using Bootstrap in Webpack Preferred way of using Bootstrap in Webpack vue.js vue.js

Preferred way of using Bootstrap in Webpack


I am not sure if this is the best way, but following work for me well with vue.js webapp. You can see the working code here.

I have included files required by bootstrap in index.html like this:

<head>  <meta charset="utf-8">  <title>Hey</title>  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">  <link rel="stylesheet" href="/static/bootstrap.css" type="text/css">  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>  <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>  <script  href="/static/bootstrap.js"></script></head>

And this works, you can execute the repo. Why I went this way was I had to customise some config in bootstrap so I had to change the variables file and build the code of bootstrap which outputted me bootstrap.js and bootstrap.cssfiles, which I am using here.


There is an alternative way suggested here by using the npm package and a webpack customisation.

First install bootstrap in your project:

npm install bootstrap@4.0.0-alpha.5

And make sure you can use sass-loader in your components:

npm install sass-loader node-sass --save-dev

now go to your webpack config file and add a sassLoader object with the following:

sassLoader: {    includePaths: [        path.resolve(projectRoot, 'node_modules/bootstrap/scss/'),    ],},

projectRoot should just point to where you can navigate to node_packages from, in my case this is: path.resolve(__dirname, '../')

Now you can use bootstrap directly in your .vue files and webpack will compile it for you when you add the following:

<style lang="scss">  @import "bootstrap";</style>


I highly recommend using bootstrap-loader. You add a config file (.bootstraprc in your root folder) where you can exclude the bootstrap elements you don't want and tell where your variables.scss and bootstrap.overrides.scss are. Define you SCSS variables, do your overrides, add your webpack entry and get on with your life.


I use webpack to build bootstrap directly from .less and .scss files. This allows customizing bootstrap by overriding the source in .less/.scss and still get all the benefits of webpack.

Your code is missing an entry point for any .css/.less/.scss files. You need to include an entry point for the compiled css files. For this entry point, I declare an array with const and then include paths inside the array to the source files I want webpack to compile.

Currently, I'm using bootstrap 3 with a base custom template and also a 2nd custom theme. The base template uses bootstrap .less file styling and it has specific source overrides written in .less files.

The 2nd custom theme uses .sass file styling and has similar overrides to bootstrap's base written in .scss files. So, I need to try to optimize all this styling for production (currently coming in about 400kb but that's a little heavy because we choose to avoid CDN's due to targeting use in China).

Below is a reference webpack.config.js which works to build from .less/.scss/.css files, and also does a few other things like build typescript modules and uses Babel for converting es6/typescript to browser compatible javascript. The output ultimately ends up in my /static/dist folder.

const path = require('path');const webpack = require('webpack');// pluginsconst ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');const ManifestPlugin = require('webpack-manifest-plugin');const MomentLocalesPlugin = require('moment-locales-webpack-plugin');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const TerserJSPlugin = require('terser-webpack-plugin');const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');// take debug mode from the environmentconst debug = (process.env.NODE_ENV !== 'production');// Development asset host (webpack dev server)const publicHost = debug ? 'http://localhost:9001' : '';const rootAssetPath = path.join(__dirname, 'src');const manifestOptions = {    publicPath: `${publicHost}/static/dist/`,  };const babelLoader = {  loader: 'babel-loader',  options: {    cacheDirectory: true,    presets: [        '@babel/preset-env'    ]  }};const app_css = [    // specific order generally matters    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'css', 'fonts', 'Roboto', 'css', 'fonts.css'),    path.join(__dirname, 'node_modules', 'font-awesome', 'css', 'font-awesome.css'),    // This is bootstrap 3.3.7 base styling writtin in .less    path.join(__dirname, 'src', 'bootstrap-template1', 'assets',  'less', '_main_full', 'bootstrap.less'),    // bootstrap theme in .scss -> src\bp\folder\theme\src\scss\styles.scss    path.join(__dirname, 'src', 'bp', 'folder', 'theme', 'src', 'scss', 'styles.scss'),    // back to .less -> 'src/bootstrap-template1/assets/less/_main_full/core.less',    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'core.less'),    // 'src/bootstrap-template1/assets/less/_main_full/components.less',    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'components.less'),    //'src/bootstrap-template1/assets/less/_main_full/colors.less',    path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'colors.less'),    // <!-- syntax highlighting in .css --> src/bp/folder/static/css/pygments.css    path.join(__dirname, 'src', 'bp', 'folder', 'static', 'css', 'pygments.css'),    // <!-- lato/ptsans font we want to serve locally --> src/fonts/googlefonts.css'    path.join(__dirname, 'src', 'fonts', 'googlefonts.css'),    // a .css style -> 'src/bp/appbase/styles/md_table_generator.css'    path.join(__dirname, 'src', 'bp', 'appbase', 'styles', 'md_table_generator.css'),    // another .css style -> hopscotch 'src/libs/hopscotch/dist/css/hopscotch.min.css'    path.join(__dirname, 'src', 'libs', 'hopscotch', 'dist', 'css', 'hopscotch.min.css'),    //LAST final custom snippet styles to ensure they take priority 'src/css/style.css',    path.join(__dirname, 'src', 'css', 'style.css')];const vendor_js = [    //'core-js',    'whatwg-fetch',];const app_js = [    // a typescript file! :)    path.join(__dirname, 'src', 'typescript', 'libs', 'md-table', 'src', 'extension.ts'),    // base bootstrap 3.3.7 javascript    path.join(__dirname, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'),    path.join(__dirname, 'src', 'main', 'app.js'),    // src/bootstrap-template1/assets/js/plugins/forms/styling/uniform.min.js'    path.join(__dirname, 'node_modules', '@imanov', 'jquery.uniform', 'src', 'js', 'jquery.uniform.js'),    // src/bootstrap-template1/assets/js/plugins/ui/moment/moment.min.js'];function recursiveIssuer(m) {  if (m.issuer) {    return recursiveIssuer(m.issuer);  } else if (m.name) {    return m.name;  } else {    return false;  }}module.exports = {    context: process.cwd(), // to automatically find tsconfig.json    // context: __dirname,    entry: {        app_css,        vendor_js,        app_js,    },    output: {        path: path.resolve(__dirname, 'dist'),        publicPath: `${publicHost}/static/dist/`,        chunkFilename: '[id].[hash:7].js',        filename: '[name].[hash:7].js'    },    resolve: {        extensions: [".webpack.js", ".web.js",".tsx", ".ts", ".js", ".css"],        alias: {            jquery$: path.resolve(__dirname, 'node_modules', 'jquery', 'dist', 'jquery.js'),        }    },    target: "web",    devtool: 'source-map',    devServer: {        // this devserver proxies all requests to my python development server except         // webpack compiled files in the `/static/dist` folder         clientLogLevel: 'warning',        contentBase: path.join(__dirname, './src'),        publicPath: 'dist',        open: true,        historyApiFallback: true,        stats: 'errors-only',        headers: {'Access-Control-Allow-Origin': '*'},        watchContentBase: true,        port: 9001,        proxy: {            '!(/dist/**/**.*)': {                target: 'http://127.0.0.1:8000',            },        },    },    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',    optimization: {        minimizer: [new TerserJSPlugin({}), new OptimizeCssAssetsPlugin({})],        splitChunks: {          cacheGroups: {            appStyles: {              name: 'app',              // https://webpack.js.org/plugins/mini-css-extract-plugin/#extracting-css-based-on-entry              test: (m, c, entry = 'app') =>                m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,              chunks: 'all',              enforce: true,            },          },        },    },    plugins: [        new webpack.ProvidePlugin({            $: 'jquery',            jQuery: 'jquery',            'window.jQuery': 'jquery',            'window.$': 'jquery'        }),        // Strip all locales from moment.js except "zh-cn"        // ("en" is built into Moment and can’t be removed)        new MomentLocalesPlugin({            localesToKeep: ['zh-cn'],        }),        new ForkTsCheckerWebpackPlugin({            tslint: true, useTypescriptIncrementalApi: true        }),        new ForkTsCheckerNotifierWebpackPlugin({ title: 'TypeScript', excludeWarnings: false }),        new MiniCssExtractPlugin({            filename: '[name].[hash:7].css',            chunkFilename: '[id].[hash:7].css',            moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.[hash:7].css`        }),         new OptimizeCssAssetsPlugin({            assetNameRegExp: /\.optimize\.css$/g,            cssProcessor: require('cssnano'),            cssProcessorPluginOptions: {            preset: ['default', { discardComments: { removeAll: true } }],            },            canPrint: true        }),        new ManifestPlugin({            ...manifestOptions        }),    ].concat(debug ? [] : [        // production webpack plugins go here        new webpack.DefinePlugin({            'process.env': {                NODE_ENV: JSON.stringify('production'),            }        }),        new ForkTsCheckerWebpackPlugin({            async: false,            useTypescriptIncrementalApi: true,            memoryLimit: 2048        }),    ]),    module: {        rules: [                {                // jinja/nunjucks templates                test: /\.jinja2$/,                loader: 'jinja-loader',                query: {                    root:'../templates'                }            },            {                test: /\.ts(x?)$/,                exclude: /node_modules/,                use: [                    babelLoader,                    {                        loader: 'ts-loader',                        options:                            {   // disable type checker - we will use it in                                // fork-ts-checker-webpack-plugin                                transpileOnly: true                            }                    }                ]            },            {                test: /\.js$/,                exclude: /node_modules/,                use: [                    babelLoader                ]            },            {                test: /\.(html|jinja2)$/,                loader: 'raw-loader'            },            {                test: /\.(sc|sa|c)ss$/,                use: [                    {                        loader: MiniCssExtractPlugin.loader,                        options: {                          hmr: debug,                            // only use if hmr is not working correctly                          // reloadAll: true,                        },                    },                    {                        loader: "css-loader",                    },                    {                        loader: "sass-loader"                    },                ]            },            {                test: /\.less$/,                use: [                    {                        loader: MiniCssExtractPlugin.loader,                        options: {                          hmr: debug,                            // only use if hmr is not working correctly                          // reloadAll: true,                        },                    },                    {                        loader: 'css-loader', // translates CSS into CommonJS                    },                    {                        loader: 'less-loader', // compiles Less to CSS                    },                ],            },            {                test: /\.(ttf|eot|svg|gif|ico)$/,                use: [                    {                        loader: 'file-loader',                        options: {                            name: '[path][name].[hash:7].[ext]',                            context: rootAssetPath                        },                    },                ],            },            {                test: /\.(jpe?g|png)$/i,                loader: 'responsive-loader',                options: {                    name: '[path][name].[hash:7].[ext]',                    adapter: require('responsive-loader/sharp'),                    context: rootAssetPath                }            },            {                test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,                 loader:'url-loader',                 options:{                    limit: 10000,                    mimetype: 'application/font-woff',                    // name: ('fonts/[path][name].[hash:7].[ext]'),                    name: ('fonts/[name].[hash:7].[ext]'),                 }            },            {                test: require.resolve("jquery"),                use:[                    { loader: "expose-loader", options:"$" },                    { loader: "expose-loader", options:"jQuery" },                    { loader: "expose-loader", options:"jquery" }                ]            }        ]    },};