Skip to content

Commit

Permalink
Expands next/script documentation (vercel#31063)
Browse files Browse the repository at this point in the history
This PR does the following to partially address vercel#31062:

- Expands the [Script Component](https://nextjs.org/docs/basic-features/script) page in the core documentation
- Adds a `next/script` API reference page
  • Loading branch information
housseindjirdeh authored and natew committed Feb 16, 2022
1 parent 83d4389 commit 0c52c17
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 87 deletions.
70 changes: 70 additions & 0 deletions docs/api-reference/next/script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
description: Optimize loading of third-party scripts with the built-in Script component.
---

# next/script

<details>
<summary><b>Examples</b></summary>
<ul>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-tag-manager">Google Tag Manager</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-analytics">Google Analytics</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel">Facebook Pixel</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-clerk">Clerk</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-segment-analytics">Segment Analytics</a></li>
</ul>
</details>

<details>
<summary><b>Version History</b></summary>

| Version | Changes |
| --------- | ------------------------- |
| `v11.0.0` | `next/script` introduced. |

</details>

> **Note: This is API documentation for the Script Component. For a feature overview and usage information for scripts in Next.js, please see [Script Optimization](/docs/basic-features/script.md).**
## Optional Props

### src

A path string specifying the URL of an external script. This can be either an absolute external URL or an internal path.

### strategy

The loading strategy of the script.

| `strategy` | **Description** |
| ------------------- | ---------------------------------------------------------- |
| `beforeInteractive` | Load script before the page becomes interactive |
| `afterInteractive` | Load script immediately after the page becomes interactive |
| `lazyOnload` | Load script during browser idle time |

### onLoad

A method that returns additional JavaScript that should be executed after the script has finished loading.

The following is an example of how to use the `onLoad` property:

```jsx
import { useState } from 'react'
import Script from 'next/script'

export default function Home() {
const [stripe, setStripe] = useState(null)

return (
<>
<Script
id="stripe-js"
src="https://js.stripe.com/v3/"
onLoad={() => {
setStripe({ stripe: window.Stripe('pk_test_12345') })
}}
/>
</>
)
}
```
201 changes: 114 additions & 87 deletions docs/basic-features/script.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ description: Next.js helps you optimize loading third-party scripts with the bui

# Script Component

<details>
<summary><b>Examples</b></summary>
<ul>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-tag-manager">Google Tag Manager</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-analytics">Google Analytics</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel">Facebook Pixel</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-clerk">Clerk</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-segment-analytics">Segment Analytics</a></li>
</ul>
</details>

<details>
<summary><b>Version History</b></summary>

Expand All @@ -13,102 +24,135 @@ description: Next.js helps you optimize loading third-party scripts with the bui

</details>

The Next.js Script component enables developers to set the loading priority of third-party scripts to save developer time and improve loading performance.
The Next.js Script component, [`next/script`](/docs/api-reference/next/script.md), is an extension of the HTML `<script>` element. It enables developers to set the loading priority of third-party scripts anywhere in their application without needing to append directly to `next/head`, saving developer time while improving loading performance.

```jsx
import Script from 'next/script'

export default function Home() {
return (
<>
<Script src="https://www.google-analytics.com/analytics.js" />
</>
)
}
```

Websites often need third parties for things like analytics, ads, customer support widgets, and consent management. However, these scripts tend to be heavy on loading performance and can drag down the user experience. Developers often struggle to decide where to place them in an application for optimal loading.
## Overview

With `next/script`, you can define the `strategy` property and Next.js will optimize loading for the script:
Websites often use third-party scripts to include different types of functionality into their site, such as analytics, ads, customer support widgets, and consent management. However, this can introduce problems that impact both user and developer experience:

- `beforeInteractive`: For critical scripts that need to be fetched and executed **before** the page is interactive, such as bot detection and consent management. These scripts are injected into the initial HTML from the server and run before self-bundled JavaScript is executed.
- `afterInteractive` (**default**): For scripts that can fetch and execute **after** the page is interactive, such as tag managers and analytics. These scripts are injected on the client-side and will run after hydration.
- `lazyOnload` For scripts that can wait to load during idle time, such as chat support and social media widgets.
- Some third-party scripts are heavy on loading performance and can drag down the user experience, especially if they are render-blocking and delay any page content from loading
- Developers often struggle to decide where to place third-party scripts in an application to ensure optimal loading

> **Note:**
>
> - `<Script>` supports inline scripts with `afterInteractive` and `lazyOnload` strategy.
> - Inline scripts wrapped with `<Script>` _require an `id` attribute to be defined_ to track and optimize the script.
The Script component makes it easier for developers to place a third-party script anywhere in their application while taking care of optimizing its loading strategy.

## Usage

Previously, you needed to define `script` tags inside the `Head` of your Next.js page.
To add a third-party script to your application, import the `next/script` component:

```js
// Before
```jsx
import Script from 'next/script'
```

// pages/index.js
import Head from 'next/head'
### Strategy

export default function Home() {
return (
<>
<Head>
<script async src="https://www.google-analytics.com/analytics.js" />
</Head>
</>
)
}
With `next/script`, you decide when to load your third-party script by using the `strategy` property:

```jsx
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />
```

Now, you use `next/script` in the body of your Next.js page. It has client-side functionality that decides when and how to load the remote script based on the `strategy`.
There are three different loading strategies that can be used:

> **Note:**
>
> - `next/script` **must not** be placed in either a `next/head` component or in `pages/_document.js`.
- `beforeInteractive`: Load before the page is interactive
- `afterInteractive`: (**default**): Load immediately after the page becomes interactive
- `lazyOnload`: Load during idle time

```js
// After
#### beforeInteractive

// pages/index.js
import Script from 'next/script'
Scripts that load with the `beforeInteractive` strategy are injected into the initial HTML from the server and run before self-bundled JavaScript is executed. This strategy should be used for any critical scripts that need to be fetched and executed before the page is interactive.

export default function Home() {
return (
<>
<Script src="https://www.google-analytics.com/analytics.js" />
</>
)
}
```jsx
<Script
src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js"
strategy="beforeInteractive"
/>
```

## Examples
Examples of scripts that should be loaded as soon as possible with this strategy include:

### Loading Polyfills
- Bot detectors
- Cookie consent managers

```js
import Script from 'next/script'
#### afterInteractive

export default function Home() {
return (
<>
<Script
src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
strategy="beforeInteractive"
/>
</>
)
}
Scripts that use the `afterInteractive` strategy are injected client-side and will run after Next.js hydrates the page. This strategy should be used for scripts that do not need to load as soon as possible and can be fetched and executed immediately after the page is interactive.

```jsx
<Script
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer', 'GTM-XXXXXX');
`,
}}
/>
```

### Lazy-Loading
Examples of scripts that are good candidates to load immediately after the page becomes interactive include:

```js
import Script from 'next/script'
- Tag managers
- Analytics

export default function Home() {
return (
<>
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
/>
</>
)
}
#### lazyOnload

Scripts that use the `lazyOnload` strategy are loaded late after all resources have been fetched and during idle time. This strategy should be used for background or low priority scripts that do not need to load before or immediately after a page becomes interactive.

```jsx
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />
```

Examples of scripts that do not need to load immediately and can be lazy-loaded include:

- Chat support plugins
- Social media widgets

### Inline Scripts

Inline scripts, or scripts not loaded from an external file, are also supported by the Script component. They can be written by placing the JavaScript within curly braces:

```jsx
<Script id="show-banner" strategy="lazyOnload">
{`document.getElementById('banner').classList.remove('hidden')`}
</Script>
```

Or by using the `dangerouslySetInnerHTML` property:

```jsx
<Script
id="show-banner"
dangerouslySetInnerHTML={{
__html: `document.getElementById('banner').classList.remove('hidden')`,
}}
/>
```

There are two limitations to be aware of when using the Script component for inline scripts:

- Only the `afterInteractive` and `lazyOnload` strategies can be used. The `beforeInteractive` loading strategy injects the contents of an external script into the initial HTML response. Inline scripts already do this, which is why **the `beforeInteractive` strategy cannot be used with inline scripts.**
- An `id` attribute must be defined in order for Next.js to track and optimize the script

### Executing Code After Loading (`onLoad`)

```js
Some third-party scripts require users to run JavaScript code after the script has finished loading in order to instantiate content or call a function. If you are loading a script with either `beforeInteractive` or `afterInteractive` as a loading strategy, you can execute code after it has loaded using the `onLoad` property:

```jsx
import { useState } from 'react'
import Script from 'next/script'

Expand All @@ -129,28 +173,11 @@ export default function Home() {
}
```

### Inline Scripts

```js
import Script from 'next/script'

<Script id="show-banner" strategy="lazyOnload">
{`document.getElementById('banner').classList.remove('hidden')`}
</Script>

// or

<Script
id="show-banner"
dangerouslySetInnerHTML={{
__html: `document.getElementById('banner').classList.remove('hidden')`
}}
/>
```
### Additional Attributes

### Forwarding Attributes
There are many DOM attributes that can be assigned to a `<script>` element that are not used by the Script component, like [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) or [custom data attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*). Including any additional attributes will automatically forward it to the final, optimized `<script>` element that is outputted to the page.

```js
```jsx
import Script from 'next/script'

export default function Home() {
Expand Down
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@
"title": "next/image",
"path": "/docs/api-reference/next/image.md"
},
{
"title": "next/script",
"path": "/docs/api-reference/next/script.md"
},
{
"title": "next/head",
"path": "/docs/api-reference/next/head.md"
Expand Down

0 comments on commit 0c52c17

Please sign in to comment.