In my previous post I talked about what what your PageSpeed Insight score means. You may be asking however, how can I speed my site up? Here are some tips I use to speed up my site, especially the Speed Index and First Meaningful Paint.

1. Add only your Critical CSS to your <head> section

Rather than loading all your CSS in the <head> section. Only load the CSS needed to load the above the fold content. This makes sure the content loads quicker, as it doesn't have to download larger stylesheets. The content will then be styled by the Critical CSS until your full stylesheet is loaded later on at the end of the body.

Don't worry, you don't need to go through your Stylesheets trying to work out which should be crtical. There's a couple of easy ways, if you're using gulp, you can use the gulp-critical-css. You can then add some code to your gulpfile to automatically create your Critical CSS for you.

There are also websites that can generate your Critical CSS for you, such as the Critical Path CSS Generator by Jonas Ohlsson Aden. (The basis of this is penthouse, a Node package which generates your Critical CSS)

Before applying Critical CSS

<head>
  <link rel=stylesheet href=myawesome.css/>
</head>
<body>
  <!-- your content -->
</body>

After applying Critical CSS

<head>
  <style>
    /* The styles needed for above the fold content */
  </style>
</head>
<body>
  <!-- your content -->
  <link rel=stylesheet href=myawesome.css/>
</body>

2. Preload your CSS

The next tip is to load the CSS asynchronously. Using a JS library such as loadCSS you can load your CSS after the page loads. This stops a page being slowed down by the download of stylesheets. Using loadCSS isn't a totally necessary step, but it helps with browser compatibility.

I generally keep the loadCSS library inline in the body. Followed by the stylesheet tags, with a little modification. First you need to change your rel to preload and add some onload scripts. Next you need to add a normal stylesheet embed inside a <noscript> tag so that users without JS still have a stylesheet.

So, extending from the above example, what would our page look like now?

<head>
  <style>
    /* The styles needed for above the fold content */
  </style>
</head>
<body>
  <!-- your content -->
  <link rel=preload href=myawesome.css as="style" onload="this.onload=null;this.rel='stylesheet'"/>
<script>
  /* loadCSS and loadCSS polyfill for rel=preload */
</script>
  <noscript>
    <link rel=stylesheet href=myawesome.css/>
  </noscript>
</body>

3. Move your JS to the end of the body

If your JS is in your <head> section, a browser will stop loading your webpage until the JS has downloaded and executed. Then it will continue to load the webpage. This will slow down your First Contentful Paint. Putting your script tags before the closing </body> tag will help this. As it wont download and execute the JS until it's finished rendering the body.

4. Defer or Async your JS

Following moving it to the end of the body, add async or defer attributes to JS you don't need to load in any particular order.

Async

Adding the async attribute to your script tags will allow the browser to carry on rendering the document, while downloading and executing the JS file. The downside to this is that it will execute as soon as it downloads. So if the file has or is a dependancy, it may execute before the dependancy has loaded. Yet if you have some standalone JS which isn't or doesn't have any dependancies, the async attribute will be perfect.

Defer

Adding the defer attribute to your script tag is very similar to the async attribute. Rather than downloading and executing whilst the browser carries on rendering the document, it will wait until the document has finished, then download and execute the JS.

5. Load your web fonts with WebFontLoader

If you're using webfonts on your website or app, you may notice when you load the page, the text disappears until the font has downloaded. Using WebFontLoader we'll be able to easily embed any external fonts into our page. It works well with Google Fonts or Typekit, but you can use your own fonts too.

So, let me give you an example of the JavaScript setup just before the closing </body> tag:

<script>
WebFontConfig = {
  google: {
    families: ['Roboto', 'Roboto:black'],
  },
  custom: {
    families: ['Foco', 'Font Awesome 5 Pro'],
      urls: ['//pro.fontawesome.com/releases/v5.6.3/css/all.css']
    },
    fontinactive: function(family, fvd) {
      if (family === 'Font Awesome 5 Pro') {
        WebFont.load({
      custom: {
        families: ['Font Awesome 5 Pro'],
        urls: ['/assets/fonts/fontawesome-pro/css/all.min.css']
      }
    })
  }
},
timeout: 2000,
  active: function() {
    sessionStorage.fonts = true;
  }
};
(function(d) {
  var wf = d.createElement('script'),
    s = d.scripts[0];
  wf.src = '//cdnjs.cloudflare.com/ajax/libs/webfont/1.6.27/webfontloader.js';
  wf.async = true;
  s.parentNode.insertBefore(wf, s);
})(document);
</script>

What does this actually do? Well, to start, we dont need to have any CSS to pull in Google Fonts, all you need to do is include the Fonts in the 'google' object. In this example were pulling in Roboto and Roboto:black. It's also similar for TypeKit fonts. If you're using custom fonts, you can pull them in one of two ways: the normal way through @font-face, or in the 'custom' object. As you can see in the example above, we are using a font called Foco. This is being specified in the CSS in a @font-face. Whereas Font Awesome 5 Pro CSS is being pulled in via a URL.

Next we have an if statement to fallover if the CDN fails. Then we set a timelimit for a font to load before falling back, which is currently set to 2 seconds. We then set a sessionStorage variable to say they have downloaded the fonts.

Finally we load in the JS library asyncronously.

Next, in the CSS you need to specify which fonts you want to use before and after the fonts have loaded. You can do this as follows:

body {
  font-family: 'Helvetica', Arial, sans-serif;
  .wf-active & {
    font-family: 'Roboto', sans-serif;
  }
}

This loads the font Helvetica, Arial or any other non fancy font. Once the web fonts have finished loading, WebFontLoader will add the class wf-active to the body. At this point the font will be replaced with the Webfont.

Then in the <head> section we have the following JS:

<script>
(function() {
  if (sessionStorage.fonts) {
    document.documentElement.className += ' wf-active';
  }
})();
</script>

This simply adds the class wf-active to the body, as font fonts should already be cached. So your fonts should show when a page loads straight away.

So what does all the above actually do?

When a user loads the page, they will see any text with the system fonts specified. Once the document is loaded and the JavaScript is executed. (Which should be before the closing </body> tag) The user should then see the fonts replaced with web fonts.

6. Pre-connect or Pre-fetch the DNS for external resource locations

Next, we can save some time asking for resources and waiting for a DNS lookup, and with preconnect, start the TSL and TCP negotitation. Then, once you request the resource from the remote location, it only has to download the file.

preconnect doesn't have the browser support of dns-prefetch. When the browser goes to download the file, it will take longer than preconnect due to the TLS and TCP negotiation.

I usually use preconnect, most main browser support this feature. I'm looking at you IE (ergh).

It's really simple to add this to your page, simply add the following to your <head> section:

<!-- preconnect tag -->
<link href="//pro.fontawesome.com" rel="preconnect" crossorigin>
<!-- dns-prefetch tag -->
<link href="//pro.fontawesome.com" rel="dns-prefetch" crossorigin>

7. Enable compression on your web server

This ones a quickie, make sure you have compression enabled on your server. If you are in control of your own servers, all you need to do is edit your webserv config.

You can check whether your server already has gzip enabled by running a test over on GT Metrix.

KeyCDN have a great guide on how to enable this on your server.

8. Optimise your images

Optimising images is really important to a websites performace. I could write a blog post just on this. But I'll just give a few tips.

  • Make sure the iamge matches the largest resolution it needs to be.
  • Downgrade the quailty and increase blur to decrease filesize.
  • Pick the correct format, make sure you're not saving large resolution images in a lossless format.

There's lots of other things you can do too. Use some next gen formats, dynamically change image sizes etc.

9. Keep your advertising and tracking to a minimum

I know this is easier said than done, however a users experience can be ruined by a slow page load speed. This experience can be made worse when it's not the content they're looking for, or something they can't see at all.

It's something to keep in mind more than anything. A users experience is the most important aspect to keep a user engaged and to make them return again.