WordPress Critical CSS Generation and Inline Delivery with PHP

Render-blocking stylesheets are one of the most common LCP and FCP bottlenecks on WordPress sites. The solution — extracting the CSS required to render above-the-fold content (“critical CSS”) and inlining it in <head> while loading the full stylesheet asynchronously — eliminates the render-blocking request entirely. The challenge is automating extraction and keeping the critical CSS up to date as theme styles change.

Problem: WordPress generates a full page of HTML and CSS on every request — the full stylesheet is downloaded before the browser can paint the above-fold content, even though only a fraction of CSS is needed for the initial viewport.

Solution: Automate critical CSS extraction using the critical npm package in your build pipeline — it loads the page in a headless browser and extracts only the CSS rules needed for the initial viewport. Inline the critical CSS via wp_add_inline_style() and load the full stylesheet asynchronously using rel="preload" + onload.


The code below generates critical CSS with critical (a Node.js CLI tool), stores it per post type, inlines it via wp_head, and loads the full stylesheet non-blocking using the media swap trick.


# Install the critical CSS CLI tool
npm install -g critical

# Generate critical CSS for the homepage (viewport 1300×900)
critical https://helloadmin.com/ \
    --width 1300 --height 900 \
    --inline false \
    --css wp-content/themes/my-theme/style.css \
    > wp-content/themes/my-theme/critical/home.css

# Generate for single posts
critical "https://helloadmin.com/?p=1" \
    --width 1300 --height 900 \
    --inline false \
    --css wp-content/themes/my-theme/style.css \
    > wp-content/themes/my-theme/critical/single.css


' . file_get_contents( $critical_file ) . '' . "\n"; // phpcs:ignore WordPress.WP.AlternativeFunctions
    }
}, 1 );

// Remove the normal blocking stylesheet and replace with async load
add_action( 'wp_enqueue_scripts', function () {
    // Remove default enqueued stylesheet
    wp_dequeue_style( 'my-theme-style' );
}, 100 );

// Add a non-blocking preload link + noscript fallback
add_action( 'wp_head', function () {
    $stylesheet = get_stylesheet_uri();
    // media="print" + onload swap is the standard async-CSS pattern
    printf(
        '' . "\n"
        . '' . "\n",
        esc_url( $stylesheet )
    );
}, 5 );


NOTE: Regenerate the critical CSS files whenever the theme stylesheet changes — stale critical CSS causes flash-of-unstyled-content (FOUC) as the page repaints when the full stylesheet loads; automate regeneration in your deployment pipeline with a post-deploy critical CLI step.