@vyvrhel/critical-css-import-webpack-plugin

Webpack plugin that creates critical CSS from flagged imports in Sass/Less file.

Usage no npm install needed!

<script type="module">
  import vyvrhelCriticalCssImportWebpackPlugin from 'https://cdn.skypack.dev/@vyvrhel/critical-css-import-webpack-plugin';
</script>

README

Critical CSS Import Webpack Plugin

Webpack plugin that simplifies manual maintaining of critical CSS(s). Plugin gets critical @imports from given Sass/Less file and adds them as a separate entry. Critical @imports are flagged by special comment.

Install / Usage / Options / Use case / Notes

Install

npm install @vyvrhel/critical-css-import-webpack-plugin --save-dev

Usage

Register plugin in webpack configuration with proper options (set source file and one critical id at least):

webpack.config.js

const CriticalCssImportPlugin = require('@vyvrhel/critical-css-import-webpack-plugin');

module.exports = {
  // ..
  plugins: [
    // ...
    new CriticalCssImportPlugin ({
      source: 'main.scss',
      criticals: [ 'home', 'blog', 'article' ], // list of critical ids
    }),
  ],
};

Flag critical @imports by adding special comment with critical CSS ids:

main.scss

@import 'tools/mixins'; // critical: all
@import 'components/molecules/articles'; // critical: blog, article
@import 'components/organisms/header'; // critical: all
@import 'components/organisms/footer';
@import 'components/pages/home'; // critical: home
@import 'components/pages/blog'; // critical: blog
// ...

Comment syntax

"critical: " ( critical-id ( "," critical-id)* ) | "all"

  • Notation critical: followed by comma-separated list of critical ids.
  • For including @import in all critical CSS use keyword all instead of critical id.
  • Comment format can be change by pattern option.

Result

Now, depending on your webpack workflow, application should output files like home.critical.css, blog.critical.css and article.critical.css.

In other words, plugin simulates source files with flagged @imports and adds them as separate entries. In the words of webpack config, plugin basically does the following on background:

module.exports = {
  // ...
  entry: {
    // ...
    'home.critical': /* main.scss only with imports flagged as 'home' or 'all' */,
    'blog.critical': /* main.scss only with imports flagged as 'blog or 'all' */,
    'article.critical': /* main.scss only with imports flagged as 'article or 'all' */,
  }
  // ...
};

Entry names for each critical id can be changed by criticals options.

Options

source

Type: {String} Required

Path to source Less/Sass file.

criticals

Type: {Array|Object|String} Required

Array of critical CSS objects:

[
  {
    id: 'critical-name-1',
    entry: 'critical-name-1.critical'
  },
  {
    id: 'critical-name-2',
    entry: 'critical-name-2.critical'
  },
]
  • id
    Type: {String} Required
    The unique identifier for a critical CSS (used in CSS comment).

  • entry
    Type: {String} Default: [id].critical
    Webpack entry name.

Possible shortcuts:

// string (one critical CSS with default entry name)
'critical-name'

// array of strings (more critical CSSs with default entry name)
[ 'critical-name-1', 'critical-name-2' ]

// object (one critical CSS)
{ id: 'critical-name' } // with default entry name
{ id: 'critical-name', entry : 'entry-name' }

deleteJsOutput

Type: {Boolean} Default: true

If enabled then .js output file that is generated together with .css file by webpack will be deleted.

pattern

Type: {Function} Default:

(criticalId) => new RegExp(`critical: ([a-zA-Z0-9\_\-]*, )*(all|${criticalId})(,|;|$)`, 'g')

Function returning regular expression used to filter out @imports from source file.

Use Case

Without plugin

We have one main Sass/Less file with many imports:

main.scss

// common styles for whole project
@import 'settings/colors';
@import 'tools/mixins';
@import 'components/molecule/articles';
@import 'components/organism/header';
@import 'components/organism/footer';
@import 'components/page/home';
@import 'components/page/blog';
// ...

We also have partially duplicated file(s) that imports only critical styles:

home.critical.scss

// critical styles for homepage
@import 'tools/mixins';
@import 'components/organism/header';
@import 'components/page/home';
// ...

blog.critical.scss

// critical styles for blog page
@import 'tools/mixins';
@import 'components/molecule/articles';
@import 'components/organism/header';
@import 'components/page/blog';
// ...

article.critical.scss

// critical styles for article page
@import 'tools/mixins';
@import 'components/molecule/articles';
@import 'components/organism/header';
// ...

All processed by webpack:

webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './main.scss',
  module: {
    rules: [
      {
        test: /\.scss$/i,
        use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin(),
  ],
};

With plugin

Maintaining these multiple files can be annoying, therefore plugin allows us to handle critical styles directly within main file:

main.scss

// common styles for whole project
@import 'tools/mixins'; // critical: all
@import 'components/molecule/articles'; // critical: blog, article
@import 'components/organism/header'; // critical: all
@import 'components/organism/footer';
@import 'components/page/home'; // critical: home
@import 'components/page/blog'; // critical: blog
// ...

webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CriticalCssImportPlugin = require('@vyvrhel/critical-css-import-webpack-plugin');

module.exports = {
  entry: './main.scss',
  module: {
    rules: [
      {
        test: /\.scss$/i,
        use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin(),
    new CriticalCssImportPlugin ({
      source: './main.scss',
      criticals: [ 'home', 'blog', 'article' ],
    }),
  ],
};

Now, with correctly configured plugin, files home.critical.scss, blog.critical.scss and article.critical.scss are automaticaly simulated by webpack.

Notes

Further optimization of generated critical CSS is recommended (e.g. by removing background-urls).