DIY inline critical CSS for WordPress

by Daniel Bachhuber on Dec 12, 2021

An emoji reacting to Lighthouse's "Eliminate render-blocking resources" warning.

One of Google Lighthouse’s peskiest problems is “Eliminate render-blocking resources.” Here’s the nut of it:

  1. Your style.css file can easily end up as 100+ kilobytes.
  2. Downloading this stylesheet blocks page render.
  3. Because Google wants to display the page as quickly as possible, Lighthouse throws a warning if your stylesheet is greater than 14kb.

The established solution is to extract and inline your critical CSS. Essentially, you put styles used above the fold in an inline <style></style> tag, and delay loading the main stylesheet until the page is rendered.

A graphic explaining how to pull the critical CSS from a stylesheet and inline it.

It’s relatively straightforward to generate critical CSS for a static site. Not so much for WordPress! WordPress has dynamic content on each page. If you only scrape the template file for your HTML, you’ll miss the markup in the dynamic content. Plus, production data is likely different than local data, so the HTML needs to be generated in production.

Not satisfied with the alternatives, we built our own solution.

TinyBit Critical CSS

Our solution for inline critical CSS on WordPress is two-fold:

  • tinybit-critical-css-server (“the server”) is a Node.js application that can run on Google Cloud Functions. It performs the critical CSS calculation.
  • tinybit-critical-css-plugin (“the plugin”) is a WordPress plugin that communicates with the server and integrates the critical CSS into your site.

At a high level, here’s how the system works:

  1. The process is initiated by WP-CLI or the refresh webhook.
  2. When triggered, the plugin does a server-side render of the given WordPress page. After the page is rendered, its HTML and CSS are sent to the server.
  3. The server renders the HTML and CSS inside a headless browser to compute the critical CSS.
  4. When the server returns a successful response, the plugin stores critical CSS to the filesystem.
  5. Now that the critical CSS exists, it is included inline and the stylesheet is deferred.

One key element to our solution: server-side page renders. The plugin renders and captures the HTML page directly in the generation process. This lets us avoid some complexities: the server needing to bypass cache, the site needing to be web-accessible to the server, etc. Instead, the plugin is able to provide exactly the HTML and CSS to be evaluated.

Our approach is also opt-in on a page-by-page basis. If you have lots of varying pages, you may only care about inline critical CSS for your landing pages.

Alternatives

There are at least a couple of alternatives you could consider: WP Rocket and Autoptimize.

WP Rocket appears to use a similar approach with its own hosted service. There are a few differences, though. First, instead of rendering the page server-side, WP Rocket makes a request back to your site. This request has to bypass cache entirely in order to have the most up-to-date HTML and CSS. Second, WP Rocket picks a somewhat random assortment of pages, so you’ll probably need to tune that. Third, WP Rocket is a large plugin with lots of bells and whistles you may not want in the context of solving this problem.

Autoptimize is a similar implementation to WP Rocket, except it instead requires a paid subscription to criticalcss.com. Autoptimize too does a lot more than critical CSS, so be prepared for configuration tweaking.

If you don’t want to maintain your own Node.js server, both of these would be reasonable to consider.


Made it all the way to the end? Come join TinyBit as our first UX Engineer!