DEV Community

Cover image for ✏ Handle CSS in webpack | Extract CSS
Francesco Di Donato
Francesco Di Donato

Posted on

✏ Handle CSS in webpack | Extract CSS

These post is the second of a trilogy.

🔎 Focus on 🖌 CSS Handling Parts
(Index) #️⃣
development only inline CSS 1️⃣
both dev & prod mini-css-extract-plugin 📌
production only CSS modules 3️⃣

Example Code 📜

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 adist folder.

package.json
{
 ...
 "scripts": {
    "start": "webpack-dev-server --env development",
    "build": "webpack --env production"
  },
 ...
}
Enter fullscreen mode Exit fullscreen mode

- 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
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);
};
Enter fullscreen mode Exit fullscreen mode

- 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
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 };
};
Enter fullscreen mode Exit fullscreen mode

- 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
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),
});
Enter fullscreen mode Exit fullscreen mode

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

Top comments (2)

Collapse
 
yellow1912 profile image
yellow1912

Coming from very traditional web development background, webpack is weird. Im uber confused after reading its document: It suggests that I import the css/saas file from a js file? WTF? Then if I want to separate the result css files I need to use a plugin to extract? What? Please tell me that I'm wrong, because I think that is just plain stupid.

Collapse
 
zbendas profile image
Zachariah Bendas

I'm also new to Webpack, but the gist I've gotten from it is this: Webpack's goal is to make it easy to compile your development files into simple build products through a pipeline that you define in its configuration. By default, Webpack will read in your .CSS files and then use JS to compile and inject them directly into the build product.

This specifics of this injection leads to a big, crowded <style> tag, which can be ugly for production environments. The extractor plugin used in the article above pulls that <style> tag out and builds a discrete .CSS file instead, passing that into your eventual build products like one would normally do for a stylesheet.

The advantage of this process is that you can use a mix of compiled and uncompiled Sass (or your favorite CSS preprocessor), any frameworks (Bootstrap, for example, is written in Sass/SCSS), and plain CSS when you're styling your site. Webpack handles the nitty-gritty of compiling it into one solid piece of CSS, and then injects or extracts it according to your config.

Webpack also has some neat tricks for minimizing your build products and optimizing their structure, to attempt to help your site be as performant as possible, though mileage may vary on that front.

All these features make it a very versatile, powerful tool!