Frontend Development 10 min read

Extracting and Inlining Critical CSS with Webpack for Faster First Paint

This article explains how to identify, extract, and inline critical CSS using the Critical Node.js module and a custom Webpack plugin, while deferring non‑critical CSS loading to improve first meaningful paint and overall page performance.

Hujiang Technology
Hujiang Technology
Hujiang Technology
Extracting and Inlining Critical CSS with Webpack for Faster First Paint

Google PageSpeed Insights recommends eliminating render‑blocking CSS and JavaScript to let users see content as early as possible. This guide shows how to programmatically separate critical CSS (required for the initial view) from non‑critical CSS and load them efficiently using Webpack.

What Is Render‑Blocking?

Resources marked as render‑blocking prevent the browser from displaying the page until they are downloaded and processed. Adding CSS via <link rel="stylesheet" href="/style.css"> in the <head> blocks rendering, especially for large frameworks like Bootstrap.

Critical CSS

Critical CSS is the subset needed for the first screen. It should be inlined in the document head, while the rest of the styles are loaded later.

.nav { ... }
.jumbotron { ... }
.btn { ... }

Non‑critical CSS (e.g., modal styles) can be placed in a separate file.

.modal { ... }

Sample Project Setup

The project uses Bootstrap SASS, sass-loader , ExtractTextPlugin , and HtmlWebpackPlugin to bundle assets.

main.js

require("bootstrap-sass/assets/stylesheets/_bootstrap.scss");

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: ['css-loader', 'sass-loader']
        })
      },
      ...
    ]
  },
  plugins: [
    new ExtractTextPlugin({ filename: 'style.css' }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    })
  ]
};

The generated index.html initially includes the CSS in the <head> , which blocks rendering.

Programmatically Identifying Critical CSS

The critical Node.js module (by Addy Osmani) extracts critical CSS by rendering the page at a given viewport size with PhantomJS and collecting used rules.

const critical = require("critical");
critical.generate({
  base: path.join(path.resolve(__dirname), 'dist/'),
  src: 'index.html',
  dest: 'index.html',
  inline: true,
  extract: true,
  width: 375,
  height: 565,
  penthouse: { blockJSRequests: false }
});

The output HTML inlines the critical CSS inside a <style> tag and adds a preload link for the non‑critical stylesheet:

<style type="text/css">
  /* critical CSS */
  body { ... }
</style>
<link href="/style.96106fab.css" rel="preload" as="style" onload="this.rel='stylesheet'">
<noscript><link href="/style.96106fab.css" rel="stylesheet"></noscript>

Inline Critical CSS and Preload Non‑Critical CSS

Inlining removes the need for an extra request for the critical rules, while rel="preload" fetches the remaining stylesheet without blocking rendering. A small script swaps the preload link to a regular stylesheet once it loads.

Adding the Critical Plugin to Webpack

A custom html-critical-webpack-plugin wraps the critical module and runs after HtmlWebpackPlugin finishes.

const HtmlCriticalPlugin = require("html-critical-webpack-plugin");
module.exports = {
  ...
  plugins: [
    new HtmlWebpackPlugin({ ... }),
    new ExtractTextPlugin({ ... }),
    new HtmlCriticalPlugin({
      base: path.join(path.resolve(__dirname), 'dist/'),
      src: 'index.html',
      dest: 'index.html',
      inline: true,
      minify: true,
      extract: true,
      width: 375,
      height: 565,
      penthouse: { blockJSRequests: false }
    })
  ]
};

Note: Use this plugin only for production builds because it slows down development.

Performance Results

Testing with Chrome Lighthouse shows that extracting critical CSS reduces First Meaningful Paint by almost one second and speeds up Time to Interactive by about 0.5 seconds, though actual gains depend on the size of the original stylesheet.

Overall, separating critical from non‑critical CSS with Webpack and the Critical plugin significantly improves perceived load performance for front‑end applications.

frontendperformanceWebpackCSScritical CSS
Hujiang Technology
Written by

Hujiang Technology

We focus on the real-world challenges developers face, delivering authentic, practical content and a direct platform for technical networking among developers.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.