How to load styles from NPM modules using Webpack
I spent some time figuring out how to load Bootstrap styles from
node_modules
in a Webpack build, so here’s how I finally got it to
work. The demo project used here is available on GitHub with a
step-by-step walkthrough of the changes.
Project setup
Let’s assume we start out with a project like this:
myProject/
node_modules/bootstrap/dist/css/bootstrap.css
src/
js/
main.js
styles/
main.scss
index.html
package.json
webpack.config.js
src/index.html
:
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> Greeting of the world </title>
</head>
<body>
<h1> Hello, World! </h1>
<button class="btn btn-success">
<span class="glyphicon glyphicon-off"></span>
I'm a button
</button>
<script type="application/javascript" src="bundle.js"></script>
</body>
</html>
src/js/main.js
:
console.log('Hello, World!');
src/styles/main.scss
:
body {
background-color: "#101010";
color: "#606060";
}
package.json
(relevant parts):
{
"devDependencies": {
"jshint": "^2.8.0",
"jshint-loader": "^0.8.3",
"webpack": "^1.12.2",
"webpack-dev-server": "^1.12.1"
},
"dependencies": {
"bootstrap": "^3.3.5"
}
}
webpack.config.js
:
'use strict';
var path = require('path');
var webpack = require('webpack');
var SRC_DIR = path.resolve(__dirname, 'src');
var BUILD_DIR = path.resolve(__dirname, 'build');
module.exports = {
context: SRC_DIR,
entry: {
app: './js/main.js',
},
output: {
path: BUILD_DIR,
filename: 'bundle.js',
},
resolve: {
root: SRC_DIR,
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'jshint',
exclude: /node_modules|bower_components|lib/,
},
],
},
};
Getting to work
Alright, first of all let’s build the project and take a look.
$ npm install
$ ./node_modules/.bin/webpack-dev-server --content-base src/
You should see the “Hello, World!” message on the page and in the console, and an unstyled button. So far so good.
Let’s add some styles! We’ll do this by loading our styles as a module in
main.js
:
'use strict';
require('styles/main');
console.log("Hello, World!");
For this to work, we’ll need to add some loaders to our Webpack configuration. First, we install the loaders and their peer dependencies:
$ npm install --save-dev style-loader css-loader sass-loader node-sass
Then we add these loaders to our Webpack configuration, and also tell the
resolver to look for files with the .scss
extension:
module.exports = {
// ...
resolve: {
// ...
extensions: ['', '.min.js', '.js', '.json', '.scss'],
},
module: {
loaders: [
// ...
{
test: /\.css$/,
loaders: ['style', 'css'],
},
{
test: /\.scss$/,
loaders: ['style', 'css', 'sass'],
},
],
},
We restart webpack-dev-server
and see that our styles from main.scss
have
been loaded!
Now let’s load those Bootstrap styles to make your button pretty. This is the
part that took me some time to figure out. We’ll do that is by adding an
@import
rule to main.scss
. The ~
in the import path tells the Webpack
loader to look for the bootstrap
module in node_modules
instead of trying to
resolve it as a relative path:
@import "~bootstrap/dist/css/bootstrap.css";
However, building our project now greets us with a bunch of errors:
ERROR in ../~/css-loader!../~/sass-loader!./styles/main.scss
Module not found: Error: Cannot resolve 'file' or 'directory' ../fonts/glyphicons-halflings-regular.eot in ./src/styles
@ ../~/css-loader!../~/sass-loader!./styles/main.scss 6:4386-4438 6:4461-4513
ERROR in ../~/css-loader!../~/sass-loader!./styles/main.scss
Module not found: Error: Cannot resolve 'file' or 'directory' ../fonts/glyphicons-halflings-regular.woff2 in ./src/styles
@ ../~/css-loader!../~/sass-loader!./styles/main.scss 6:4565-4619
ERROR in ../~/css-loader!../~/sass-loader!./styles/main.scss
Module not found: Error: Cannot resolve 'file' or 'directory' ../fonts/glyphicons-halflings-regular.woff in ./src/styles
@ ../~/css-loader!../~/sass-loader!./styles/main.scss 6:4652-4705
ERROR in ../~/css-loader!../~/sass-loader!./styles/main.scss
Module not found: Error: Cannot resolve 'file' or 'directory' ../fonts/glyphicons-halflings-regular.ttf in ./src/styles
@ ../~/css-loader!../~/sass-loader!./styles/main.scss 6:4737-4789
ERROR in ../~/css-loader!../~/sass-loader!./styles/main.scss
Module not found: Error: Cannot resolve 'file' or 'directory' ../fonts/glyphicons-halflings-regular.svg in ./src/styles
@ ../~/css-loader!../~/sass-loader!./styles/main.scss 6:4825-4877
This is because bootstrap.css
references font files using url('<foo>')
, and
Webpack tries to resolve and inline those files into the CSS bundle. The
solution is, again, to add more loaders. The one we need in this case is
url-loader
:
$ npm install --save-dev url-loader file-loader
Let’s add it to the Webpack configuration:
module.exports = {
// ...
module: {
loaders: [
// ...
{
test: /\.(woff|woff2|ttf|svg|eot)/,
loader: 'url?limit=100000',
},
]
}
}
After restarting the webpack-dev-server
we see that the Bootstrap styles and
fonts are now loaded successfully!