-
SummaryWe would like to introduce a new API in Gatsby that aids in loading third-party scripts performantly. The intention is to offer:
To be consistent we'll refer to this API as a "script component" from here on out. You can follow progress on this RFC via its milestone. Try it outThe Gatsby See the release notes and reference documentation for usage information.
MotivationMany important services are used by adding script tags to the DOM, and while doing this isn't particularly difficult, doing so without degrading website responsiveness and related search ranking can prove more difficult. At present, Gatsby users have access to the tools required to solve for performant script loading (e.g. native The goal of this feature is to ease this pain by offering a high-level API with performant defaults for third-party script loading. Basic exampleUsage could look something like: Note - Strategy names, example scripts and interfaces below are for illustration, they may change. import * as React from 'react';
import { Script, ScriptStrategy } from 'gatsby';
const GTM = `G-XXXXXXXXXX`; // Example Google Analytics 4 identifier
// Example script sources for illustration
const scripts = {
dayjs: 'https://unpkg.com/browse/[email protected]/dayjs.min.js',
three: 'https://unpkg.com/[email protected]/build/three.js',
marked: 'https://cdn.jsdelivr.net/npm/marked/marked.min.js',
gtag: `https://www.googletagmanager.com/gtag/js?id=${GTM}`,
};
// Strategy prop is optional, defaults to post-hydrate
function IndexPage() {
return (
<main>
<h1>Script component proof of concept</h1>
<Script src={scripts.three} strategy={ScriptStrategy.postHydrate} />
<Script src={scripts.marked} strategy={ScriptStrategy.idle} />
<Script src={scripts.gtag} strategy={ScriptStrategy.offMainThread} forward={[`gtag`]} debug={true} />
<Script id="gtag-config" strategy={ScriptStrategy.offMainThread}>
{`
// Example configuration of Google Analytics for use in Partytown
window.dataLayer = window.dataLayer || [];
window.gtag = function gtag() { window.dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', ${GTM}, { send_page_view: false })
`}
</Script>
<Script id="my-unique-id" dangerouslySetInnerHTML={{ __html: `alert('Hello world')` }} />
<Script id="my-unique-id-2">{`alert('Hello world')`}</Script>
</main>
);
}
export default IndexPage; Detailed designConceptually it's useful to think of strategies that can be defined for script loading on initial page visit:
Here's a hypothetical scenario illustrating what that would look like: In addition, an experimental strategy that loads scripts in a web worker via Partytown will be explored. This is not illustrated in the image above. Proposed in scope
Drawbacks
Alternatives
By comparison, these are the reasons why a JSX component implementation could be preferable:
Rollout
How we teach thisWe can communicate this generally as an extension of the HTML We would need to update Gatsby’s existing documentation on third party script loading and add a new page(s) detailing usage. In addition, we should update existing examples in the Gatsby monorepo if need be and include a new one demonstrating usage. Questions for youIn addition to your thoughts generally, we would love to get your views specifically on:
Thank you and we look forward to your thoughts and feedback! |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 13 replies
-
I really like this idea, having used other React platforms that implement similar APIs has always made for a great user experience, and in most cases I think reduces the barrier to entry for people trying to learn the platform because they just use the component out of the box rather than needed to understand Browser, SSR, and other APIs to achieve the same thing. One question: Would the "idle" attribute ensure that it loads long enough after the rest of the page that the script loading times wouldn't be factored into auditing tools like lighthouse? After running some tests on a clients gatsby site I found we could gain an extra 20-30 performance points if that were the case, which would be a major boost! |
Beta Was this translation helpful? Give feedback.
-
I think this is a great idea because of two reasons:
Big thumbs up from me. As for the questions: "If you think the illustrated strategy names (e.g. pre-hydrate, post-hydrate, idle, experimental-partytown) communicate the behavior effectively or if other names can describe it better (e.g. before-interactive, after-interactive, etc.)" I personally like the illustrated names better (eg. pre-hydrate etc) because it is easier to relate to the update cycle in gatsby, otherwise before-interactive etc would have to be translated to pre-hydrate when thinking in technical terms. The drawback is of course that people who aren't familiar with hydrate won't understand it out of the box. But maybe that pushes them to learn it. "Whether you think a dedicated API for dependent scripts (e.g. load script B after A loads) is something that should be included as a feature in scope or not" I think this is a great idea but I don't think it needs to be part of the first scope. Once it is out and adopted by people, then it can be assessed if it is needed or not. "If you think Gatsby should export a TypeScript enum that can be used to declare strategies (as shown in the example) or not" I don't think this is needed, and as far as I've seen I haven't seen this pattern used anywhere else. Just a typescript interface/type with |
Beta Was this translation helpful? Give feedback.
-
A simple question for "If you think Gatsby should export a TypeScript enum that can be used to declare strategies (as shown in the example) or not" When would this ever not be the case? Why would there be any appetite for potentially not doing this? |
Beta Was this translation helpful? Give feedback.
-
Hi all, I've updated the RFC above with a section ("Try it out") on how to try out the latest canary release! New canary versions will be added to the list in that section as they become available and will note what they include. As per the feedback from @tinglof, @herecydev and @marko-hologram on the strategy TypeScript interface, both string literals and the enum can be used to define strategies. |
Beta Was this translation helpful? Give feedback.
-
We'd love to hear any feedback you all have about the new script component that @tyhopp released as a canary (see RFC description for installation instruction). In case you'd like to give feedback, but are unsure what kind of feedback we're looking for, here are some suggested steps to try:
Thanks again for giving it a try! |
Beta Was this translation helpful? Give feedback.
-
I'll dive into this a bit more; just some initial thoughts:
Is it really that difficult at the moment? You have an SSR api where you can use The example shows that this is done on each page, that requires either:
It might be helpful to outline your expectations here. What's optimal? From my experience scripts rarely change between pages, so ideally I would like to do this using option 3 As an aside, is there similar investment being applied to meta and links? These are things that do often change between pages and currently the recommendation is "use react helmet". Which feels unloved and is a another pile of libraries/plugins to get working. Instead it would be great to see something like this. That to me should be much higher priority if you're after improving DX |
Beta Was this translation helpful? Give feedback.
-
This feature is available in 4.15 now 🎉 https://www.gatsbyjs.com/docs/reference/release-notes/v4.15/#script-component |
Beta Was this translation helpful? Give feedback.
-
A suggestion: how about having an option to put the script either in body or head, as of now, it only puts it in the body of the document. However, some of our content creators always look for such scripts in the head of the document. It will be suitable if we have an option for that. |
Beta Was this translation helpful? Give feedback.
This feature is available in 4.15 now 🎉
https://www.gatsbyjs.com/docs/reference/release-notes/v4.15/#script-component