✏ Handle CSS in webpack | Extract CSS
Tags: webpack, css, javascript, beginners
Published: 2020-08-05 09:15:13

These post is the second of a trilogy.

| 🔎 Focus on | 🖌 CSS Handling | Parts |
|:-:|:-:|:-:|
| (*Index*) | | [#️⃣](https://dev.to/didof/handle-css-in-webpack-index-34kj) |
| *development only* | *inline CSS* | [1️⃣](https://dev.to/didof/handle-css-in-webpack-inline-css-fho) |
| **both dev & prod** | **mini-css-extract-plugin** |📌|
| *production only* | *CSS modules* | 3️⃣ |

### Example Code 📜
> [webpack-extract-css](https://github.com/didof/webpack-extract-css)

---

# Final Product 🤖
By completing this starge you will get a good starting point for your personal webpack configuration. In production mode it is better not to inject CSS directly into the bundle because you can get a *Flash of Unstyled Content* (**FOUC**) - the inlined CSS it's applied only when the bundle it's executed.
We implement the extraction of CSS in a separate file that is executed at the same time as the rest.

---

# Flow of Thought 🏮

1. Add *build* script
2. Download the *loader*
3. Create the loader *function*
4. Connect to *useRules*
5. Add the plugin in *webpack.config.js*

---

## Implementation 🤓

### - 1 - Add *build* script
In `packages.json` add the *build* script that will bundle our code and store it in a`dist` folder.
##### package.json
```json
{
 ...
 "scripts": {
    "start": "webpack-dev-server --env development",
    "build": "webpack --env production"
  },
 ...
}
```

### - 2 - Download the *loader*
In the terminal invoke `npm i -D mini-css-extract-plugin`.

### - 3 - Create the loader *function*
Add the package just downloaded in `loaders.js`. Then exports a new function named *extractCSS* - it's almost the sameto the one built in the first phase. The difference stays in the fact that *style-loader* is is replaced with *MiniCssExtractPlugin.loader*.
##### loaders.js
```js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// ... stage one created functions

exports.extractCSS = (config = {}) => {
	// basic rule
	const rule = {
		test: /\.css$/i,
		use: [MiniCssExtractPlugin.loader, 'css-loader'],
	};

	return addConfigs(rule, config);
};
```

### - 4 - Connect to *useRules*
In *useRules* the implementation is fair simple:
1. import the just created function
2. add its case in the *loaders* object
3. add the instruction relative to production in *instructions* object
##### useRules.js
```js
const { loadCSS, extractCSS } = require('./loaders'); //[1]

module.exports = (env) => {
	const loaders = {
		css: (i) => {
			switch (i) {
				case 'inline':
					return loadCSS();
				case 'MCEP': //[2]
					return extractCSS();
				default:
					throw new Error(`The instruction ${i} is not covered`);
			}
		},
	};

	// developer interface
	const instructions = {
		css: {
			development: 'inline',
			production: 'MCEP', //[3] Mini-Css-Extract-Plugin
		},
	};

	// business logic - already seen in stage one
	let message = '[useRules] ';
	const rules = Object.entries(instructions).map(([key, value]) => {
		const i = instructions[key][env];
		message += key + '|' + i;
		return loaders[key](i);
	});

	console.info(message);
	return { rules };
};
```

### - 5 - Add the plugin in *webpack.config.js*
In order to properly work, `MiniCssExtractPlugin` need to be imported [1] and loaded [2] in the plugins section in `webpack.config.js`:
##### webpack.config.js
```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
 // [1]
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const useRules = require('./config/useRules');

module.exports = (env) => ({
	devServer: {
		open: true,
		stats: 'errors-only',
	},
	plugins: [
		new HtmlWebpackPlugin({
			title: 'Webpack Inline CSS',
		}),
      new MiniCssExtractPlugin({ // [2]
         filename: '[name].[hash].css'
      })
	],
	module: useRules(env),
});
```

---

# Checking the Outcome 😎
1. `npm start`: the web server will start and open up your bundle in a tab. Open *devTools* and peek in *Network* section. Reload the page. See? There is no trace of any CSS file - it is installed in the JavaScript.
2. `npm run build`: a `dist` folder will be generated. Get into that and serve it `cd dist && serve` (you may need to `npm i serve -g` before). Now, the result is the same as before **but** open again the *devTools*, get in *network*. You see that? It's a separate CSS file. No more *Flash of Unstyled Content*!

> In console you should see log such as `[useRules] css|inline` or `[useRules] css|MCEP`

---

## Upgrade the **last-stage** 🔝
> avaiable soon