How we achieved 100/100 pagespeed & CWV, with third party scripts
Published: 2023-03-22 11:53:08

It's no secret that Google's Pagespeed metrics, most notable [Core Web Vitals (CWV)](https://web.dev/vitals/) is a core ranking factor in SEO and improves your quality score for AdWords (making your clicks cheaper).

At [Accreditly](https://accreditly.io/), we recently launched our new website, and pagespeed and CWV were imperative as part of the launch. 

We also needed to utilise a number of third party scripts on the website. Scripts we know will slow things down substantially, and in our tests caused us to fail Total Blocking Time (TBT) and sometimes fail First Contentful Paint (FCP) core web vitals metrics.

So, how did we do it? We'll talk through what we did to achieve 100/100 below.

![100/100 Pagespeed score](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ulf0gusieh3xevaa4fh0.png)

## Fast by default

The first thing to remember is to consider pagespeed with everything you do. Don't think of speed as something you go back and retrospectively fix afterwards. As you build the page out you should be checking your Lighthouse scores regularly. 

It can be extremely difficult to diagnose a problem causing a slow page load after the site build is complete, but if you check regularly enough you can easily spot when there is a drop.

## Don't forget about CLS

[CLS](https://web.dev/cls/) is one of the easiest metrics to fail on, 
one of the easiest to prevent, but can be one of the more challenging to retrospectively fix, especially if you're using a CMS.

CLS is Cumulative Layout Shift. It's an index/score of how much the page bounces around during page load. The best score is `0.0`, which happens with no bouncing at all.

{% embed https://www.youtube.com/watch?v=jHybl6PxDk8 %}

The most common culprits for bouncing pages are assets being loaded without informing the browser of the space you expect them to occupy. The most obvious example of this is images. The easy fix is to ensure all images have a `width` and `height` associated. This tells the browser how big to expect the image to be before the image has loaded.

Responsive images are a little more tricky, but some CSS rules should be able to override the `width` and `height`. Take a look at the images on our home page for how to achieve this, as there are a few tricks used in different circumstances, including utilising `object-fit`.

Other causes for CLS include font loading, [this post](https://simonhearne.com/2021/layout-shifts-webfonts/) has a lot of useful information on how to prevent CLS caused by custom fonts.

## A proper build pipeline

We use [Vite](https://vitejs.dev/) for our build pipeline. Vite does a number of things, but mainly it:
- Compiles [Tailwind CSS](https://tailwindcss.com/) for us, based on template files used and strips out any unused styles. This keeps our CSS to an absolute minimum.
- Minifies our CSS.
- Versions our CSS, so any changes we make are always live and no users see outdated styling. It does this by naming files with a unique hash.
- Compiles our JS in a similar way to our CSS, minifying it and versioning it.
- Splits our JS into multiple chinked files, which makes them much faster to download on browsers supporting HTTP/2.
- Reads our CSS, inline styles/classes (we're using Tailwind 3 that supports JIT classes that could contain file names), and HTML/template files for image paths. These are then copied, versioned and lossless compression applied to keep filesizes down.

All of these things are automated, so on every deployment everything 'just works' and keeps itself as lean as possible. A deployment takes around 10 seconds.

## Don't use JavaScript unless you have to

Long gone are the days of using jQuery on every project. But there still remains a desire by developers to use JavaScript frequently, even if a lot of it can be done in CSS (animations, for example) or isn't needed at all.

We use Laravel as our backend framework. Laravel is great, but even on a vanilla install it ships with lo-dash and axios by default, neither of which we actually use, so ensure you remove anything you aren't using. Remove it from your `app.js` entry file, but also from your `package.json` file so it isn't loaded in the first place.

We then use JavaScript very sparingly, we have a couple of very small vanilla JS scripts we use here and there for adding and removing classes, but for the most part it's extremely lean.

## Load JS where you need it

Our application allows [web developers to sit exams on various technologies to earn certifications](https://accreditly.io/certifications). These exams are interactive web pages. They require JavaScript to work, and make a lot of client-server requests regularly.

To make development easier we used the best technology available to us. As such, we use [Livewire](https://laravel-livewire.com/), which allows us to build out components easily for the exam system.

Livewire is great, but it adds a substantial overhead. It has its own vendor styles and scripts, and then the exam system itself that we wrote has a lot to it.

The fix for this is pretty straightforward, we have split out the build pipeline within Vite for all of the files for the exam system. It has its own entry JS file, it's own entry file for CSS, which generate it's own set of files with Vite. We then only load these files on the exam area of the website. This means that unless you're logged in, enrolled on an exam and taking an exam we're not burdening you with this substantial overhead. 

We have done something similar with code highlighting, which we use a lot on our articles and the exams, but adds a very large overhead for CSS and JS.

This has kept our public CSS and JS tiny.

## Use Cloudflare

We use Cloudflare. It's a great service, and it's free.

Notably, we use Cloudflare to minify our HTML (our CSS and JS is already minified) and we use RocketLoader, which speeds up our JS execution a little. RocketLoader doesn't help with our CWV or Lighthouse/Pagespeed scores, because they're already lean enough to pass with flying colours, but it does help with the exam JS.

We also use Cloudflare to set efficient caching policies. All of our assets are given a 1 year expiry, which is a header that Cloudflare sends. We opted for this rather than doing it from our server directly as it gives us an interface to cache-bust certain files should we ever need to update them.

All of our CSS and JS is versioned, so we never need to cache bust those files.

Cloudflare also offers a great CDN for all of our assets. It's made a big difference for TTFB (time to first byte) for those users who aren't geographically close to our servers.

## Zaraz for third-party scripts

[Zaraz](https://www.cloudflare.com/en-gb/products/zaraz/) is a product by Cloudflare. It's a very similar to Google Tag Manager, which manages third party scripts and injects them onto the page.

Third-party scripts are the worst nightmare for any developer trying to achieve a good pagespeed score. You can hit 100/100, introduce some third party scripts and drop to below 50/100.

Zaraz fixes this by offloading all of the script processing to Cloudflare Workers. The scripts all get run in the cloud, and the output is piped to the relevant third party, but it's all done away from the user's browser, keeping everything fast.

If you're interested in Zaraz we have written a comprehensive guide about [Cloudflare Zaraz vs Google Tag Manager (GTM)](https://accreditly.io/articles/cloudflare-zaraz-vs-google-tag-manager) which is worth a read.

## Summary

When you apply these techniques, along with following a number of frontend best practices it's not as difficult as it used to be to achieve a great Pagespeed score and to smash all of the Core Web Vitals requirements. 🚀