Why is page speed important?
Page speed is a measurement of how fast the content on an individual page loads. In as competitive an arena as the internet, perhaps the most important and visceral separator is how fast a website page loads. When a page loads faster it tends to have higher conversion and higher perceived business competency. Page speed has also become important statistically, as conversion rates correspond directly to page load according to an article from Portent:
- Website conversion rates drop by an average of 4.42% with each additional second of load time (between seconds 0-5)
- Website conversion rates drop by an average of 2.11% with each additional second of load time (between seconds 0-9)
With these statistics and countless others, page speed is something to strive to continually improve. In this article, several methods are presented, but it is by no means an exhaustive list of everything that can be done.
How to test page speed?
There are many ways to test page speed. A few examples are Pingdom and WebPagetest. My favorite and recommended option is Lighthouse, which is an open-source, automated tool. It has audits for performance, accessibility, progressive web apps, SEO and more. It can even be run from Google Chrome devtools. We have an article that goes into the greater details of devtools if more info is needed.
Why only improve WordPress?
To be clear, most of the page speed improvements below are technology agnostic and can/should be used on any site. However, WordPress-specific improvements seemed like a different animal because it can be more difficult to approach. Choosing WordPress is a tradeoff, where the development team gives up complete control over the codebase, and in exchange the marketing team gains a Content Management System (CMS). With less codebase control, the development team will need to go about some page speed improvements a little differently.
It’s also of note that much of the internet is built using WordPress. As of writing this article, WordPress has a 39% market share of all websites. That’s a 300% increase since 2011, when it was only 13%. I personally don’t foresee any reason why this would start going in the other direction anytime soon.
5 ways to improve WordPress page speed
1) Async/Defer WordPress Enqueued Scripts
By default, WordPress enqueues and loads all theme scripts synchronously. These critical assets will delay page render until these assets are loaded, decreasing page speed as a result. It is possible to async or defer these assets, depending on your need. As a reminder of each:
Async
These scripts can download while the webpage is being downloaded, and can execute either before or after the webpage has finished rendering. This may be best for independent scripts, like ads, as they are not critical to the webpage functionality.
Defer
These scripts execute after the document has loaded and parsed, leading to a faster page load feel. Unfortunately, without these scripts the page may not be functional, so use these in non-critical JavaScript or add a loading feature so the user knows that the JavaScript hasn’t completed loading.
In order to accomplish this change, update the wp_enqueue_script function, and add a separate function.
The wp_enqueue_script function will change by adding either #asyncload or #deferload to the end of the script URL:
From:
<?php
wp_enqueue_script( 'script-name', get_template_directory_uri() . '/js/example.js', array(), '1.0.0', true );
to either:
<?php
wp_enqueue_script( 'script-name', get_template_directory_uri() . '/js/example.js#asyncload', array(), '1.0.0', true );
or:
<?php
wp_enqueue_script( 'script-name', get_template_directory_uri() . '/js/example.js#deferload', array(), '1.0.0', true );
The second part is to add WordPress functions using the clean_url filter to check for the asyncload and deferload strings and update the script if present. These functions should be added into the WordPress functions.php file.
<?php
// Async scripts - use by adding "#asyncload" to the URL
function zz_async_scripts($url) {
if ( strpos( $url, '#asyncload') === false )
return $url;
else if ( is_admin() )
return str_replace( '#asyncload', '', $url );
else
return str_replace( '#asyncload', '', $url )."' async='async";
}
add_filter('clean_url', 'zz_async_scripts', 11, 1);
// Defer scripts - use by adding "#deferload" to the URL
function zz_defer_scripts($url) {
if ( strpos( $url, '#deferload') === false )
return $url;
else if ( is_admin() )
return str_replace( '#deferload', '', $url );
else
return str_replace( '#deferload', '', $url )."' defer='defer";
}
add_filter('clean_url', 'zz_defer_scripts', 11, 1);
2) Utilize image srcset
What srcset does is allow a browser to adjust the image loaded for the user based on the browser window width. The sizes and images are defined in an image or picture tag along with the width of the window to know when to use those different images. The great thing about WordPress is srcset is now built into their core. WordPress can even generate the srcset and sizes automatically. Below is example code:
<?php
$id = YOUR_IMAGE_ID;
$src = wp_get_attachment_image_src( $id, 'full' );
$srcset = wp_get_attachment_image_srcset( $id, 'full' );
$sizes = wp_get_attachment_image_sizes( $id, 'full' );
$alt = get_post_meta( $id, '_wp_attachment_image_alt', true);
?>
<img src="<?php echo $src[0];?>"
srcset="<?php echo esc_attr( $srcset ); ?>"
sizes="<?php echo esc_attr( $sizes );?>"
alt="<?php echo esc_attr( $alt );?>" />
3) Lazy load images
Lazy loading images is more common and accepted than ever. It seems that visitors now have grown accustomed to sometimes waiting for images to load as they scroll, and at least for me that’s okay because I know that was a tradeoff to allow the page to initially load quicker.
There isn’t a WordPress-specific way of lazy loading images that I know of, as everyone I’ve seen is written in JavaScript. We have been using the same script across all projects, no matter the technology. It utilizes the Intersection Observer API along with a fallback for Internet Explorer 11. In order to make it work, a class of “lazy” will need to be added to the image, and then we will add “data-“ to the src/srcset/sizes attributes. Below is the code:
/*
// HOW TO USE
//
// 1) Add class "lazy" to the image
// 2) Add "data-" to the src/srcset/sizes attributes
//
// Example:
// <img data-src="<?php echo $src[0];?>" data-srcset="<?php echo esc_attr( $srcset ); ?>" data-sizes="<?php echo esc_attr( $sizes );?>" alt="<?php echo esc_attr( $alt );?>" class="lazy" />
*/
let lazyImgs = document.getElementsByClassName('lazy'),
lazyObserverOptions = {
rootMargin: '200px', //This option tells the browser to trigger the observer and load the image when the user is 200px away from the image. Update based on site needs.
};
if (!!lazyImgs.length) {
let isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (!!isIE11) { // IE11
for (var i = 0; i < lazyImgs.length; i++) {
if (!!(lazyImgs[i] instanceof HTMLImageElement) && !!lazyImgs[i].classList.contains('lazy')) {
lazyImgs[i].dataset.src ? lazyImgs[i].src = lazyImgs[i].dataset.src : ''
}
}
} else { // Modern Browsers
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let el = entry.target;
if (!!(el instanceof HTMLImageElement) && !!el.classList.contains('lazy')) {
el.dataset.src ? el.src = el.dataset.src : ''
el.dataset.srcset ? el.srcset = el.dataset.srcset : ''
el.dataset.sizes ? el.sizes = el.dataset.sizes : ''
}
observer.unobserve(el);
}
});
}, lazyObserverOptions);
for (let img of lazyImgs) {
observer.observe(img);
}
}
}
4) Lazy load background images
When a page loads, if an element has a background-image style the browser will block rendering to go fetch that image. However, this may not be needed if, for example, the image is far down the page. In this case, lazy loading background images can be utilized. The code we use is very similar to lazy loading regular images, where a data attribute is checked and then JavaScript adds in the image style. This can be triggered utilizing the Intersection Observer API or triggered after page load. Code is below:
<div class="background-image-element" data-background-image="image.jpg">...</div>
<script type="text/javascript">
const backgroundElement = document.querySelector('.background-image-element')
backgroundElement.dataset.backgroundImage ? backgroundElement.style.backgroundImage = `url(${backgroundElement.dataset.backgroundImage})` : ''
</script>
5) Utilize font-display: swap
Font-display instructs a browser on how fonts are loaded and displayed by a browser. Specifically, the swap value instructs the browser to use a fallback until the custom font is available to use. The best use-case I’ve seen for this is with Google fonts, where they now offer swap as a default when utilizing either their <link> or @import options for the web. If these are used for a sans-serif font, like Open Sans for example, a website will show the default sans-serif font until the custom font has finished downloaded and is available for use, at which point the website will switch the font used to Open Sans.
Bonus! Remove Gutenberg Block Library CSS
If you are like the millions of others that are still using the Classic Editor, then you’re loading Gutenberg assets on your website that your visitors do not need. Utilize these wp_dequeue_style functions to prevent those from loading. Every request and asset that a visitor doesn’t have to load is a savings in page speed.
// Remove Gutenberg Block Library CSS from loading on the frontend
wp_dequeue_style('wp-block-library');
wp_dequeue_style('wp-block-library-theme');
wp_dequeue_style('wc-block-style'); // Removes WooCommerce block CSS
Conclusion
The ways to improve page speed presented in this article are not all encompassing, but hopefully they can help! It must also be stressed that page speed is not something that is set and forget. As sites grow and evolve the speed is repeatedly revisited to try and improve. In addition, as new page speed techniques are discovered it becomes an opportunity that businesses can capitalize on to improve their websites.