See what's launched

The next gen Gatsby is here Performance, developer velocity, and scaling to meet Enterprise needs - See what’s launched

Sign Up for Free

Using Gatsby Script Component to Decrease Page Load Times

Ty Hopp
June 9th, 2022

It’s been a challenge to performantly add scripts to websites since the dawn of time (well, since browser makers introduced the <script> tag at least!). Script execution in the browser is blocking by default, and we as developers are responsible for implementing many useful scripts in our web pages without degrading the experience for the end-user.

As of Gatsby 4.15, Gatsby is releasing a built-in <Script> component that aids in loading scripts performantly. The script component offers a convenient way to declare different loading strategies, and a default loading strategy that gives Gatsby users strong performance out of the box. Whether you want to leave the heavy lifting of managing scripts to Gatsby or you want maxiumum flexibility and control, the Gatsby <Script> component is a great tool for the job.

Script Component is usable today, and we encourage you to try it out on your Gatsby site! See the reference documentation for the Gatsby Script API for full details. In this blog post, I want to walk through some of the decisions you will make when you use the Gatsby Script component. To do that, we’re going to build a small, interactive website that allows you to write in Markdown and see it rendered in HTML on the same page. 

Before we dive in, feel free to browse or grab the source code from this blog post from the repo on GitHub. As an aside, I’ve also created a video that provides a quick demo overview of Script Component, which you can find embedded at the end of this post or view directly on YouTube

And now, onwards!

What are we building?

Let’s expand on what we’re building a bit more. You can check out the demo site deployed to Gatsby Cloud, or if you prefer to stay here’s a screenshot of what it looks like:

Script Component post-hydrate strategy

We’ll use the excellent marked module to do the markdown to HTML conversion for us. We could install it via npm, but we’d lose the ability to control loading strategies. In our case, we’re obsessed with performance, and we want maximum control to determine how our scripts are loaded.

Enter the Gatsby <Script> component. We’ll create three separate implementations that each use a different loading strategy of the script component – post-hydrate, idle, and off-main-thread. We’ll explore the nuances of each, and finally come to a conclusion on which is best for our use case.

post-hydrate-visual

Using the post-hydrate strategy

Alright, we have already created a new Gatsby site with npx create-gatsby and we want to see the script component in action before we optimize. Here’s how we import the <Script> component and load marked in the most basic way:

Note – We’ll use TypeScript for this entire post. The script component can be used the same way in JavaScript.

In many cases, this is all you’ll need to use the Gatsby script component. Gatsby will load your script after the page has hydrated by default (the post-hydrate strategy), so you get instant performance gains over the regular <script> tag in the browser, even with async or defer applied.

Let’s create a component for the markdown-to-HTML functionality that we’ll also use when we try out the idle and off-main-thread strategies:

It works! Now if we open up the developer tools in your browser and inspect the runtime performance (in Chrome DevTools this is found in the performance tab), we’ll see something like this:

Gatsby Script Component

Circled in the top right is marked, and it loads well after the framework script (hydration happens there) that loads right after the HTML document is requested.

This is good for us, because it means that the loading of marked does not increase our Total Blocking Time, and what’s most important (the document, hydration) happens first. Our users are going to want to read what’s on the page first, and by the time they have, the lightweight marked script will have loaded so the demo can be used!

Now, let’s try out the idle strategy.

Using the idle strategy

Compared to post-hydrate, the behavior of idle is very much affected by what else is happening on the main thread in the page. Under the hood the idle strategy makes use of the requestIdleCallback web API, which instructs the browser to only execute our script when the main thread is free.

In our case, we don’t have much else happening on the page for the time being, so we don’t expect to see much of a difference. It is easy to imagine down the line that our little demo might be used in a larger page, and at that point the choice between post-hydrate and idle will be something to reconsider given the specifics of the scenario.

Luckily, the Gatsby Script component makes switching between these two strategies a breeze. We only need to swap or apply a different value to the strategy attribute. In our case,

<Script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" />

becomes:

<Script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" strategy={ScriptStrategy.idle} />

Just for the sake of completeness, here’s what the performance tab shows when marked is loaded with idle:

Gatsby Script component 3

Not much difference, both are equally performant! Now let’s explore the last strategy, off-main-thread.

Using the off-main-thread strategy

Note – The off-main-thread strategy is experimental.

The off-main-thread strategy is unique in that it leverages Partytown, a library that helps relocate resource intensive scripts into a web worker. It’s an interesting solution that may or may not work with various scripts due to certain trade-offs, but has the advantage of not asking the main thread to do any significant work after the initial service worker registration. That’s exciting!

The Partytown documentation is clear about the use case it’s best at handling:

Partytown is best suited for third-party scripts such as Google Tag Manager or Facebook Pixel, since they’re only handling user events and lazily posting data to their services in the background.

That’s not an exact match for our use case, and with this in mind we are likely better off using the post-hydrate or idle strategies.

Just for fun though, let’s see if we can get it to work. Using our <Demo> component from before, we’ll create a new page and do something like this:

In addition in our gatsby-config file, we’ll declare the marked URL as safe for Gatsby to proxy for Partytown:

If we fire it up we’ll see it works! It’s not optimal and relies on a polling approach that negatively impacts our site performance (Partytown will make many requests between the main thread and service worker), but we knew from the outset that off-main-thread wasn’t the play. If we ever have to add Google Analytics or a similar script though, we’ll definitely revisit this.

Finally, a look at the performance waterfall:

Gatsby Script Component 4

As expected there is a small startup cost to request and set up the service worker, but once it is then we’re off to the races.

Wrapping up

Together we’ve walked through some of the decisions that you’ll encounter when using the Gatsby script component as a developer working on Gatsby sites. Put another way, we talked about the wonderful problem of having both power and flexibility at your fingertips with the Gatsby Script component, and how to go about analyzing its effect on your site’s performance.

I hope you enjoyed the ride, and I look forward to seeing what you’ll build with the Gatsby Script component!

Don’t forget to check out all the other exciting Gatsby product news from Launch Week, and follow our primary Gatsby Twitter account (@GatsbyJS) to stay in the loop with all the latest Gatsby news and updates.

Share on TwitterShare on LinkedInShare on FacebookShare on PinterestShare via Email

Senior Software Engineer, Framework

Tagged with Launch Week, Script ComponentView all Tags

Talk to our team of Gatsby Experts to supercharge your website performance.

Contact Gatsby Now
© 2022 Gatsby, Inc.