Optimizing the performance of third-party dependencies

Hi, my name is Katie Hempnius.

And today I'm going to be talking about optimizing the performance of your third-party dependencies, particularly as it relates to Core Web Vitals.

Third-party dependencies are huge part of most sites.

However, they're also notorious for introducing performance challenges.

Third-party dependencies is an incredibly broad topic so I'm going to narrow down this presentation to only touching on two particular aspects of third-party dependencies, and that's third-party scripts and third party fonts.

In this presentation we'll focus on optimization techniques that you can use if removing third-party dependencies is not an option.

In addition, I want to note that most, if not, all of the techniques I talk about today are also going to be applicable to first-party resources as well.

As a developer, unfortunately, you don't have control over the content of your third-party scripts, but what you do have control over is how scripts get applied to the page via the script tag.

Some of the things that you can control are where that script tag is placed on the page, how the script type gets there, (for example, is it inserted via tag manager or is it included in the initial HTML), whether that script tag uses async or defer, and lastly, the moment at which that script tag gets injected, I think all these things are sometimes choices that we make on autopilot.

However, they can still have a big and significant impact on how that script impacts the performance of your page.

Even on faster connection and higher end devices, network and CPU are typically constrained during the initial page load process.

As a result, it's very important to prioritize which resources get requested first.

The exact mechanics of how the browser takes your HTML and turns it into the ordering that it uses to request resources, um, is a little bit complex.

It varies from browser to browser.

However, as a developer, two of the factors that you do have a little bit of control over are where that third-party script or resource is placed in the page overall - for example, if it's placed at the top of the page, the bottom of the page - and also, where that script is placed relative to other resources.

Most third-party vendors tell you to place their script at the top of the head, for two reasons.

One is that that script is going to get encountered by the browser parser more quickly because the browser parser parses top to bottom, but also it increases the likelihood that that 'script' tag will be included in the initial payload returned from the server.

Keep in mind that generally speaking, the initial server response to the browser can only contain 14 kilobytes.

So, script tags further down the page may not be included in that initial response.

However, just because a script tag is in the head doesn't necessarily mean that it'll be requested quickly.

Also keep in mind the other contents of the head.

For example, if you have 10 asynchronous script tags in your head, the one that's listed first is going to be the one that's requested first.

When thinking about resource ordering the goal isn't to perfectly and delicately orchestrate the order in which the resources on our pages load.

In fact you can't do that and trying to do so will be a bad idea, but instead just look at your page, look at the traces and just ask yourself, "Does this make sense?

Are the important resources getting requested fairly early?

And are the less important resources not getting in the way of those important resources getting requested?" As an example, a pattern I see a lot is that, in looking at a site - in general, the order in which the resources get requested it makes sense, however there's like one stray library, for example, maybe it's a chat embed.

And I will say in this case, the chat embed definitely isn't the most important resource on the page and probably by accident that chat embed made its way to the top of the head and it's now one of the very first resources getting requested.

Just by moving that script tag further down the head actually can significantly improve performance because it's freeing up computing resources for the browser to download slightly more important things, rather than initially devoting all the browser's resources to downloading that chat embed.

Another variable that you have control over is the time at which a script gets injected into the page.

And you don't necessarily have to delay this a lot to see a performance impact.

Even just a second or two can have a considerable impact on the page.

And some of the variables that you can play around with here are waiting for a particular amount of time to pass, waiting for a particular performance event, for example Largest Contentful Paint, or waiting for user interaction, for example, waiting for the user to sroll on the page.

If you can, you should self-host your static resources.

It's becoming less common, but something I see a lot still is that sites will download libraries like jQuery or Bootstrap from third-party sites, rather than self hosting them on their own servers.

And if those libraries were to be self-hosted on your own servers, you could probably deliver them much more quickly because the browser doesn't have to set up that third-party connection.

In addition, I want to pause here to note that back in the day, at least in theory, there was an idea that by loading popular libraries from third-party sites, you could potentially get a performance benefit if that library was already in the user's cache, and therefore it didn't have to be loaded fresh when the user came to your site.

That no longer applies, broswers have changed how they approach caching - they now use cache partitioning - and so today a resource will only get re-used if that same resource was downloaded from the same URL in the same frame.

Browsers have done this to improve security and privacy.

If at all possible you should be loading your scripts asynchronously.

A script is only going to be asynchronous if you add the async attribute to the script.

If not, it will be synchronous and synchronous scripts are really bad for performance because when the browser encounters them, it stops what it's doing, it waits for that script to download and instead of continue to parse the page and make requests - new requests for other resources - it just sits there waiting.

And in the worst case scenario, if that synchronous script were to never download, it would prevent the rest of the page from downloading as well.

If you must use synchronous scripts - and for example, some A/B testing libraries do rely on synchronous scripts - make sure that that request resolves as quickly as possible.

Now, one of the things you can do to help with that a little bit is to use a resource hint to set up a connection with that third-party site in hopes that that synchronous request will return faster.

There's only three ways that a script make its way onto the page.

It can be included in the initial HTML.

It can be injected by another script, or it can be injected by a tag manager.

And yes, technically a tag manager is a type of script injecting another script.

If you want a script to be discovered as quickly as possible and executed as quickly as possible, you should be including it in the initial HTML.

Using scripts to inject other scripts isn't great if you need those scripts to be executed - downloaded or executed right away.

However, if you're using that technique for something like lazy loading, it can be a very powerful technique.

"How do I measure the performance impact of third-party scripts?", is a question I hear a lot and it's not perfect, but one way - good way to go about investigating that is to use request blocking.

And for example, if you have a tag manager and you want to know the performance impact of a tag on your page, you could block your tag manager from loading.

And a great way to do this is with WebPageTest.

Have WebPageTest go set up a new test, then in the settings, add your tag manager or whatever third-party you want to block.

And I would recommend - if you want to be able to draw conclusions from your results - is to run as many runs as possible.

Like for example, 10 would be ideal.

And the reason for this is that it will help eliminate some of the variants that you otherwise get in your results.

When doing this also just keep in mind A/B testing.

If your site does use A/B testing, it could be impacting the results that you get back.

And as long as you're on WebPageTest, I recommend checking out the 'Request Map' feature.

One of I think the challenges of working with third-party scripts is it can kind of be difficult to keep an eye on them because you don't develop them yourself and you don't necessarily know what they're up to.

And so the 'Request Map' feature maps out all the requests that are being made on your page and then in turn the requests that those node requests might set off, and it can be a good way to help you understand what's going on with the third-party resources on your page.

Fonts aren't necessarily something that immediately comes to mind when most people think of third-party resources.

However, third-party fonts are actually very common.

For example, if you've ever used Google Fonts, that would be an example of a third-party font provider.

And fonts in general, regardless of whether they're first-party or third-party can have a significant impact on performance.

By default browsers will delay text rendering if the associated web font for the particular piece of text has not loaded.

And in many situations, this can delay First Contentful Paint, and some situations this can delay Largest Contentful Paint.

In addition, although font swapping is great for making sure that text is immediately visible to the user and readable and useful, depending on how font swapping gets applied it can cause layout shifts.

Before diving into best practices for third-party fonts, I think it's important to start by quickly discussing how '@font-face' works and how this impacts font loading.

When you load a font from a third-party font provider, what you're actually initially loading is a stylesheet that contains one or more '@font-face' declarations and includes references to the URLs of the fonts that you will eventually want to download.

A common misconception is that the browser downloads a font as soon as it encounters that - it's corresponding '@font-face' declaration.

This is not true.

By itself, '@font-face' declaration is not enough to trigger a font download.

Rather the browser will only download a font if it is used on the page.

And what determines that is typically the styling that you write rather than the styling coming from your third-party provider.

The browser does this because it doesn't want to be downloading fonts that it doesn't need and in particular if your site supports multiple languages or uses font subsetting - which it should - any stylesheet can contain tens or even hundreds of font-face declarations.

I mention this because I want to make two points.

One is that when you're using a third-party font it's going to take at least five round trips to the server before the browser has a font file..

Um This is because first it needs to set up a connection with the third-party provider and that's three round trips, and then it needs to go download the stylesheet continue to the separation and then lastly after that on that fifth round trip it will actually finally download the font files themselves.

And because this is a pretty long process, it's important to kick off that process sooner rather than later to make sure that your fonts are available in time.

The second point is that when you're thinking about font optimization, it's just important to give some thought to the contents of your stylesheets as is to give thought to the contents of the font files themselves.

Oftentimes changing delivery or the contents of a stylesheet can have a significant impact on how the performance of the fonts on your page behave.

At least on paper, using self-hosted fonts should be better for performance because they eliminate that need to create a separate connection to connect to the third-party font provider.

However in practice, it's not quite so clearcut, and a good example of this is the fact that the Web Almanac found that sites that used third-party font providers actually had a faster render time than sites that used first-party fonts.

And again that's correlation not causation, but it helps demonstrate some of the factors and complexity impacting the performance trade-offs between first-party and third-party fonts.

If I had to generalize, I would say sites that are more serious about performance are also going to be the ones who are going to be more likely to get better performance from first-party funds are a couple of reasons for this.

The first is that to get better performance from first-party fonts, realistically your site needs to be using a CDN and it needs to be using HTTP/2.

Without this, it's very unlikely that your own servers are going to be able to return a font more quickly than a third-party font provider can.

The second piece of this is that there is a little bit of additional work entailed by using your - setting up your own fonts.

Third-party font providers do take care of some optimizations for you such as applying font subsetting, and although it's not a tonne of work to secure these optimizations on your own, it is more work than just copying and pasting a line of code into your own code.

If your site does use third-party fonts it's highly recommended that you use 'preconnect' resource hint to establish an early connection with the font provider.

The performance impact of preconnect is going to vary depending on connection latency.

On a fast connection this really might not shave off that many milliseconds from font delivery, but where it really shines is if a user is on a slower connection, having made sure it set up an early connection with preconnect can at times save up to like almost a second on delivering that font quickly.

However, keep in mind that 'preconnect' only sets up the connection with the font provider.

It does nothing to download the stylesheet or the font files themselves.

And something I see a lot is that sites will use the 'preconnect' resource hint and that's great and wonderful, but then they set up their site to load the web font really late.

And web font don't necessarily have to be the first thing that gets requested on your page, but you probably don't want your web fonts loading after five seconds either And so what I would recommend doing is just look in dev tools, run a trace on your site and figure out when your web fonts are getting requested, and if necessary make sure to move up the the link tag for loading that font higher up in the head.

Inline styling isn't necessarily a topic exclusive to third-party fonts per se, but as long as we're on the topic of optimizing third-party fonts I want to mention it because it can be a very powerful technique for improving your font performance.

As I mentioned earlier, when you use a third-party font provider, the font provider provides a stylesheet back to the browser containing the font-based declarations corresponding to the font that you want to use.

However the browser will not request any of those particular web fonts until it knows that they're going to be used on the page.

Then typically what it uses to determine this is the styling that you've written for your page and your stylesheets.

And so one way to help the browser make this determination more quickly is to inline that usage - you know the - your font styling for your page in the head of your document.

And by doing this the browser doesn't have to wait on...

most people use an external file for the stylesheet of their site.

The browser no longer has to wait for that request to return before it can make that determination.

Last but not least, generally speaking you should avoid using preload to load your web fonts.

If you know what you're doing, have at it.

But for most people inlining your font styling as I just discussed in the previous slide will ultimately be a more effective approach.

And the reason for this is that although preload is very good at making resources discoverable, it does come with the significant downside of taking away resources, and by resources that means CPU and bandwidth from other things.

And that's why we generally do not recommend preloading your web fonts.

That's all I have for today!

Thank you for listening and I hope this was helpful

Optimizing the performance of third-party dependencies

Katie Hempenius
[email protected]

Agenda

  • Scripts
  • Fonts

The <script> tag

<script src="cats.js"></script>
<script src="dogs.js" async></script>
<script src="bears.js" defer></script>

Variables that you can control:

  • Placement on page
  • Use of async & defer
  • How it gets added to page (e.g. tag manager)
  • When it gets added to page

Script placement & ordering is important

Image of a graph charting a range of load performance metrics such as CPU, Bandwidth, browser main thread tasks and long tasks

Check

— Does this resource ordering make sense?
— Are important resources being discovered and delivered quickly?

Examples of important resources:

  • Main application code
  • Page styling
  • Fonts
  • Above-the-fold images

Delayed loading

Possible loading triggers:

— Time
— Performance events
— User actions

Self-host static resources

→ Roundtrip #1
← DNS lookup
→ Roundtrip #2
← TCP handshake
→ Roundtrip #3
← TLS negotiation

Diagram outlining the stages that a browser needs to take to establish a new connection in order to download a resource from a third party site. The stages are grouped into a unifying bracket reading: Setting up a new connection is expensive.

Examples of static resources commonly loaded from third-party sites:

— Bootstrap
— FontAwesome
— jQuery

Load third-party scripts asynchronously

(whenever possible)

<script src="chat.js" async></script>

Choose the right tool for the job

Approaches to inserting scripts:

— Markup: Script is included in the document’s original HTML markup.
— Scripts: Script is injected by another script
— Tag manager

Investigate your third-party scripts

Request Blocking in WebPageTest

— Advanced Settings > Block > Block Domains
— Add domain

Image of the WebPageTest interface

WebPageTest: Request Map

Image of the WebPageTest: Request Map tool interface showing a node map of all the requests on the page of the URL that the tool user wants to investigate

  • Run WebPageTest
  • Click “Request Map” (button is located at top of the test results).

Fonts & performance

Fonts that load late can delay text rendering and cause layout shifts.

Font loading

Stylesheet from font provider:

@font-face {
 	font-family: "Open Sans";
 	src: url("/fonts/Open-Sans.woff2") format("woff2");
}

Your stylesheet:

h1 {
  font-family: “Open Sans”
}

Self hosted vs. third-party fonts

Image of the Web Almanac's Web font performance (desktop) analytics page from 2020. The results are shown on a bar chart of desktop median FCP and LCP in milliseconds for three font hosting strategies: (self-hosted, externally hosted, or both). local is 2,426 milliseconds for median FCP and 4,176 for median LCP, external is 2,034 and 3,671 respectively, and both is 2,663 and 5,044 milliseconds respectively.

Factors impacting font performance:

  • CDN usage
  • HTTP/2 adoption
  • Application of optimizations like subsetting & unicode-range

Use preconnect for third-party fonts

<head>
<link rel="preconnect" href="https://fonts.com">
</head>

Request font-related resources soon-ish

<head>
	<link rel="preconnect" href="https://fonts.com">
	<link href="https://fonts.com/TimesNewRoman" rel="stylesheet">
</head>

Use inline styling

<head>
	<style>
		body {
		font-family: "Open Sans";
		}
	</style>
</head>

Avoid using preload to load fonts

(generally speaking)
<head>
    <link rel="preload" href="https://fonts.com/Helvetica" as=”font”>
</head>

Thank you!