Critical CSS plugin

I’m looking to use webpack-plugin-critical in the webpack.config.optimize.js file, but I have no idea what settings would be needed as the example gives index.html as the src and dest, but as its WordPress its not a single page application.

Does anyone have any experience with using this (or other working packages) with WordPress and would you be able to share a working example?

Thanks!

1 Like

What about using pagespeed for nginx?
https://www.modpagespeed.com/doc/filter-prioritize-critical-css

And for static:
https://discourse.roots.io/t/removing-unused-css-with-purgecss-uncss/11586/2

You’re overthinking it imo. Just import/compile all of your critical CSS to something like critical.css and inline it with something like:

namespace App;

/**
 * Inject critical assets
 */
add_action('wp_head', function () {
    $critical = @realpath(asset_path('styles/critical.css'));
    if (file_exists($critical)) {
        echo '<style>'.@file_get_contents($critical).'</style>';
    }
}, 10);
8 Likes

Thanks for this. Is there a webpack package you would you use to generate the critical.css file? I used to use penthouse with gulp but I don’t have much experience with webpack.

I’m sure there’s something out there but depending on the project size I’d probably just resort to @importing all of the above the fold / scaffolding CSS into critical.css. I have no suggestions past that though as I haven’t had to do much more. :frowning:

So I’ve managed to make some progress using critical-webpack-plugin. I’ve added the following to the webpack.config.optimize.js file to get it to generate a critical css file and then use the above code example to inject critical assets. (Replacing path/to/site with the url to your site).

const CriticalWebpackPlugin = require('critical-webpack-plugin');
module.exports = {
plugins: [
new ImageMinPlugin(...),
new CriticalWebpackPlugin({
      src: 'http://path/to/site',
      dest: 'critical.css',
      width: 480,
      height: 800,
      pathPrefix: '/styles',
      minify: true
    }),
]
} 
1 Like

In production sage appends cache busting string to file names so this path wouldn’t work. Is there a way to load file with that cache string name?

@jasonbaciulis: For production builds a list of plain file names and
their production equivalents with hash can be found in
dist/assets.json:

{
  "scripts/customizer.js": "scripts/customizer_9ecd05b4.js",
  "styles/main.css": "styles/main_9ecd05b4.css",
  "scripts/main.js": "scripts/main_9ecd05b4.js"
}

Whoops. Yes, asset_path().

I updated my code above.

It works now but only without if (file_exists($critical)) statement. file_exists() check doesn’t work for me in another place in the theme as well.
Do you have an idea why it wouldn’t work even if a file does exist?

Also, I am not using @realpath like in your code, just an asset_path. Is this your own directive? Could you post the code for it?

Thanks.

Hi,
Did you manage to get this to work ?
On my side the critical-webpack-plugin is outputing an empty critical.css file. I think it’s because the plugin is run before all files have been built, and is parsing the site without any present css.
Didn’t you encounter this problem? How can I make sure the plugin runs after all files have been compiled ?

Nevermind, I finally got it working, using the html-critical-webpack-plugin@1.1.0 (version is important because plugin’s latest versions don’t work with webpack3).

$critical is an url, and file_exists uses only physical paths.
It will work if you use fopen instead :

add_action('wp_head', function(){
        $critical=asset_path('styles/critical.css');
        if (@fopen($critical, "r")){
          echo '<style>'.@file_get_contents($critical).'</style>';
        }
      });
4 Likes

Hey Lucas, just wanted to check in on how this ended up working. I was trying to get GoogleLab’s “Inline Critters Webpack” project working with my theme but it won’t work because it looks for html files and can’t search php.

Please correct me if I’m wrong but it sounds like this is what you got working:

  • use html-critical-webpack-plugin to extract the critical path css from the php files
  • then output that css to a file called /styles/critical.css
  • use the php action listed above (in your functions.php file I imagine) to inject that critical css into the head of the page?

Is that correct? I’m also not using roots, just vanilla wordpress right now. Though it seems like a cool project.

Hi jcklpe.

Yes, though the first point isn’t technically right : html-critical-webpack-plugin isn’t exactly extracting css from php files, but from local or remote urls.

Excellent. Would you happen to be able to share a snippet of your config file as reference or maybe a git repo where I could check out how you are doing things?

I hope that’s not asking a lot, I’m new to development and while I think I’ve got an idea of how I should set stuff up I thought I’d check for a reference.

EDIT: Also I’m already attempting to integrate it to the webpack on my own, which is bringing up a couple of additional questions.

Does this require HtmlWebpackPlugin to work?

I imagine that it does in order to have an html file to actually check right? That’s what you mean when you say it’s extracting it from local or remote urls right? So what I need to do is first configure HTMLwebpackplugin to render my php files into html files that HTMLCriticalWebpack can then reference to generate the critical.css that then can be loaded by the worpress php function right?

Also is the webpack config able to differentiate between different pages being loaded, or does it just generate critical path css for all the pages and bunldes those together in critical.css?

Again, I apologize if these are sort of dumb questions.

There you go, but it uses some variables from sage, so it might differ a little from your cfg.

webpack.config.optimize.js:

module.exports = {
  plugins: [
    new ImageminPlugin({
     ...
    }),
    new HtmlCriticalWebpackPlugin({
      base: config.paths.dist,
      src: config.devUrl,
      dest: 'styles/critical-home.css',
      inline: false,
      minify: true,
      extract: false,
      width: 375,
      height: 565,
      penthouse: {
        blockJSRequests: false,
      }
    }),
    new UglifyJsPlugin({
       ...
    })
  ],
};

No, php generates the HTML server-side and what the browser (and html-critical-webpack-plugin) get is already HTML.

In my case, the critical css is generated only for the homepage, but you can run the script on how many pages you want. You’ll just need to instantiate html-critical-webpack-plugin for each page.
It may be possible to parse multiples pages at once and extract the critical css for all these pages, but I didn’t try it for now.

1 Like

I have error with this

TypeError: Cannot read property 'afterEmit' of undefined
at HtmlCriticalWebpackPlugin.apply 

How can I fix it ?

What have you tried?

This

module.exports = {
plugins: [
new ImageminPlugin({

}),
new HtmlCriticalWebpackPlugin({
base: config.paths.dist,
src: config.devUrl,
dest: ‘styles/critical-home.css’,
inline: false,
minify: true,
extract: false,
width: 375,
height: 565,
penthouse: {
blockJSRequests: false,
}
}),
new UglifyJsPlugin({

})
],
};

Sorry, I mean what have you tried to fix it? We can try to help but we don’t want to go around in circles. Have you googled the issue? Tried to resolve it on your own? If so how?