Webpack doesn't handle styling out of the box, and you will have to use loaders and plugins to allow loading style files. In this chapter, you will set up CSS with the project and see how it works out with automatic browser refreshing. When you make a change to the CSS webpack doesn't have to force a full refresh. Instead, it can patch the CSS without one.
To load CSS, you need to use css-loader and style-loader.
css-loader goes through possible @import
and url()
lookups within the matched files and treats them as a regular ES2015 import
. If an @import
points to an external resource, css-loader skips it as only internal resources get processed further by webpack.
style-loader injects the styling through a style
element. The way it does this can be customized. It also implements the Hot Module Replacement interface providing for a pleasant development experience.
The matched files can be processed through asset modules by using the type
field at a loader definition. The feature is discussed in the Loading Assets part of the book.
Since inlining CSS isn't a good idea for production usage, it makes sense to use MiniCssExtractPlugin
to generate a separate CSS file. You will do this in the next chapter.
To get started, install the dependencies:
npm add css-loader style-loader --develop
Add a new function at the end of the part definition:
webpack.parts.js
exports.loadCSS = () => ({
module: {
rules: [
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
],
},
});
Above means that files ending with .css
should invoke the given loaders. Loaders return the new source files with transformations applied on them. They can be chained together like a pipe in Unix, and are evaluated from right to left:
styleLoader(cssLoader(input))
You also need to connect the fragment to the primary configuration:
webpack.config.js
const commonConfig = merge([
...
parts.loadCSS(),
]);
You are missing the CSS still:
src/main.css
body {
background: cornsilk;
}
To make webpack aware of the CSS, we have to refer to it from our source code:
src/index.js
import "./main.css";
...
Execute npm start
and browse to http://localhost:8080
if you are using the default port and open up main.css
and change the background color to something like lime
(background: lime
).
PostCSS allows you to perform transformations over CSS through JavaScript plugins. PostCSS is the equivalent of Babel for styling and you can find plugins for many purposes. It can even fix browser bugs like 100vh
behavior on Safari postcss-100vh-fix. PostCSS is discussed in the next chapters.
Webpack provides support for the most popular styling approaches as listed below:
For anything css-in-js related, please refer to the documentation of the specific solution. Often webpack is well supported by the options.
The _CSS Modules_ appendix discusses an approach that allows you to treat local to files by default. It avoids the scoping problem of CSS.
To get most out of css-loader, you should understand how it performs its lookups. Even though the loader handles absolute and relative imports by default, it doesn't work with root relative imports - url("/static/img/demo.png")
If you rely on root relative imports, you have to copy the files to your project as discussed in the Tidying Up chapter. copy-webpack-plugin works for this purpose, but you can also copy the files outside of webpack. The benefit of the former approach is that a Development Server can pick that up.
Any other lookup will go through webpack and it will try to evaluate the url
and @import
expressions. To disable this default behavior, set css-loader url: false
and import: false
through the loader options.
[resolve-url-loader](https://www.npmjs.com/package/resolve-url-loader) comes in handy if you use Sass or Less. It adds support for relative imports to the environments.
If you want to process css-loader imports in a specific way, you should set up importLoaders
option to a number that tells the loader how many loaders before the css-loader should be executed against the imports found. If you import other CSS files from your CSS through the @import
statement and want to process the imports through specific loaders, this technique is essential.
Consider the following import from a CSS file: @import "./variables.sass";
. To process the Sass file, you would have to write configuration:
const config = {
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: { importLoaders: 1 },
},
"sass-loader",
],
};
If you added more loaders, such as postcss-loader, to the chain, you would have to adjust the importLoaders
option accordingly.
node_modules
directory#You can load files directly from your node_modules directory. Consider Bootstrap and its usage for example: @import "~bootstrap/less/bootstrap";
. The tilde character (~
) tells webpack that it's not a relative import as by default. If tilde is included, it performs a lookup against node_modules
(default setting) although this is configurable through the resolve.modules field.
If you are using **postcss-loader**, you can skip using `~` as discussed in [postcss-loader issue tracker](https://github.com/postcss/postcss-loader/issues/166). **postcss-loader** can resolve the imports without a tilde.
Webpack can load a variety of style formats. The approaches covered here write the styling to JavaScript bundles by default.
To recap:
@import
and url()
definitions. style-loader converts it to JavaScript and implements webpack's Hot Module Replacement interface.importLoaders
option. You can lookup against node_modules
by prefixing your imports with a tilde (~
).Although the loading approach covered here is enough for development purposes, it's not ideal for production. You'll learn why and how to solve this in the next chapter by separating CSS from the source.
This book is available through Leanpub (digital), Amazon (paperback), and Kindle (digital). By purchasing the book you support the development of further content. A part of profit (~30%) goes to Tobias Koppers, the author of webpack.