I’ve been obsessed with optimizing websites for awhile, and one big hurdle to a faster website is “render-blocking” JavaScript and CSS. This came up as I was trying to get a high score on Google PageSpeed Insights.
Google recommends you move these scripts and css files to the bottom of the DOM (just before the </body>
). With CSS, it’s a little more complicated because you have to figure out the “above the fold” (ugh hate that term) styles and inline them in the <head>
. More on that in the criticalCSS section.
Assumed knowledge of enqueuing
This post assumes knowledge of enqueuing scripts/styles in WordPress. If you haven’t used these functions in WordPress, I’d highly recommend brushing up on them. Regardless of if you want to use criticalCSS or not, this is a much better way to insert styles/scripts into a theme. Here are some good resources:
Moving JavaScript to bottom of the DOM
Luckily, WordPress has built-in mechanisms to put your script at the bottom of the DOM. The 5th parameter of the wp_enqueue_script() function is a boolean $in_footer
. which tells WordPress whether or not to put it in the footer. Here’s a quick example inside your functions.php
:
wp_enqueue_script( 'mysite_main', get_template_directory_uri() . '/js/main.min.js', array('jquery'), '4.0', true );
“jQuery is still in the <head>”
By default, jQuery is enqueued in the <head>
. We can change this pretty easily in functions.php
:
wp_deregister_script('jquery');
wp_register_script('jquery', "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js", false, null, true);
wp_enqueue_script('jquery');
In this example, I also chose for the Google CDN version of jQuery. Notice again the last param set to true
, just like our enqueues above.
“jQuery is STILL in the <head>!”
Now you should see jQuery at the bottom of the DOM, right? Maybe not actually.
If you have a plugin that is dependent on jQuery and puts their script in the <head>
, WordPress will keep jQuery in the head (as it should, otherwise, you’d get a “$ is not defined” error). You have a few options:
- Edit that plugin to make sure that nothing breaks if you set
$in_footer
to true in all of the enqueues. Assuming it works, contact the plugin author so they can change it.
- Use a plugin that moves all scripts to the footer (hit or miss).
- Deregister then reregister the plugin’s scripts. This is a little more risky because if the plugin changes the name of their script, you could get some broken JavaScript.
I’m partial to option 1, as it nips the problem in the bud, assuming the plugin author is responsive on the support forum. 🙂
At this point, you may run into a brick wall. Sometimes plugins do crazy things and it’s almost impossible to get the script/style where you want it. Luckily, the more well-known plugins (i.e. Contact Form 7) do these things correctly.
Get CSS to bottom of DOM
Getting CSS in the footer in WordPress is a little more tricky. The wp_enqueue_style() function doesn’t support $in_footer
(maybe they’ll add it someday), so we’ll have to get our hands dirty. Instead of using the wp_enqueue_style() function, we are going to manually echo
the <link> tag in the functions.php
(I know, I know. Just after I lectured you on how great enqueuing is):
function styles_in_wp_foot() {
echo '<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:400,700,400italic,700italic|Bitter" type="text/css" media="all" />';
echo '<link rel="stylesheet" href="' . get_stylesheet_uri() .'" type="text/css" media="all" />';
}
add_action( 'wp_footer', 'styles_in_wp_foot' );
If you reload your site, you’ll see a flash of unstyled html until the css is loaded. We don’t want this. Enter criticalCSS.
criticalCSS
Now, we have to generate the inlined styles for the <head>
. You can use a task runner like Grunt of Gulp to do this. Below is a Grunt configuration block. Please see the grunt-criticalcss docs for full implementation.
criticalcss: {
custom: {
options: {
url: "http://localurl.vvv",
// arbitrary width and height for critical to use when looking at "above the fold" styles.
width: 1000,
height: 800,
outputfile: "inc/critical.css.php",
filename: "style.css",
buffer: 800*1024
}
}
},
Alternative to a task runner
Update: For those out there that are using a pre-built theme and don’t have a task runner, it might be easier to manually run a critcalCSS generator. Take the resulting css the tool gives you and dump it into the inc/critical.css.php
file. Every time you update your css, you’ll want to run this tool again.
Now that we have that CSS in a file, we just need to echo
the php include into the <head>
. Add this to functions.php
:
function criticalCSS_wp_head() {
echo '<style>';
include get_stylesheet_directory() . '/inc/critical.css.php';
echo '</style>';
}
add_action( 'wp_head', 'criticalCSS_wp_head' );
Wrapping up
As you see, the more scripts, styles and plugins you have installed, the more complex this can get. So keep those to a minimum, folks. 🙂
After this, you should see a huge uptick in your PageSpeed score if you are doing all the other (easier) things on the list. One of our clients’ sites has a 96/100.