.
This commit is contained in:
+1068
File diff suppressed because it is too large
Load Diff
+406
@@ -0,0 +1,406 @@
|
||||
---
|
||||
title: Form Component
|
||||
description: Learn how to use the `<Form>` component to handle form submissions and search params updates with client-side navigation.
|
||||
---
|
||||
|
||||
The `<Form>` component extends the HTML `<form>` element to provide <AppOnly>[**prefetching**](/docs/app/getting-started/linking-and-navigating#prefetching) of [loading UI](/docs/app/api-reference/file-conventions/loading),</AppOnly> **client-side navigation** on submission, and **progressive enhancement**.
|
||||
|
||||
It's useful for forms that update URL search params as it reduces the boilerplate code needed to achieve the above.
|
||||
|
||||
Basic usage:
|
||||
|
||||
<AppOnly>
|
||||
|
||||
```tsx filename="/app/ui/search.tsx" switcher
|
||||
import Form from 'next/form'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
{/* On submission, the input value will be appended to
|
||||
the URL, e.g. /search?query=abc */}
|
||||
<input name="query" />
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/ui/search.js" switcher
|
||||
import Form from 'next/form'
|
||||
|
||||
export default function Search() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
{/* On submission, the input value will be appended to
|
||||
the URL, e.g. /search?query=abc */}
|
||||
<input name="query" />
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</AppOnly>
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
```tsx filename="/ui/search.js" switcher
|
||||
import Form from 'next/form'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
{/* On submission, the input value will be appended to
|
||||
the URL, e.g. /search?query=abc */}
|
||||
<input name="query" />
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/ui/search.js" switcher
|
||||
import Form from 'next/form'
|
||||
|
||||
export default function Search() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
{/* On submission, the input value will be appended to
|
||||
the URL, e.g. /search?query=abc */}
|
||||
<input name="query" />
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
## Reference
|
||||
|
||||
The behavior of the `<Form>` component depends on whether the `action` prop is passed a `string` or `function`.
|
||||
|
||||
<AppOnly>
|
||||
|
||||
- When `action` is a **string**, the `<Form>` behaves like a native HTML form that uses a **`GET`** method. The form data is encoded into the URL as search params, and when the form is submitted, it navigates to the specified URL. In addition, Next.js:
|
||||
- [Prefetches](/docs/app/getting-started/linking-and-navigating#prefetching) the path when the form becomes visible, this preloads shared UI (e.g. `layout.js` and `loading.js`), resulting in faster navigation.
|
||||
- Performs a [client-side navigation](/docs/app/getting-started/linking-and-navigating#client-side-transitions) instead of a full page reload when the form is submitted. This retains shared UI and client-side state.
|
||||
- When `action` is a **function** (Server Action), `<Form>` behaves like a [React form](https://react.dev/reference/react-dom/components/form), executing the action when the form is submitted.
|
||||
|
||||
</AppOnly>
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
- When `action` is a **string**, the `<Form>` behaves like a native HTML form that uses a **`GET`** method. The form data is encoded into the URL as search params, and when the form is submitted, it navigates to the specified URL. In addition, Next.js:
|
||||
- Performs a [client-side navigation](/docs/app/getting-started/linking-and-navigating#client-side-transitions) instead of a full page reload when the form is submitted. This retains shared UI and client-side state.
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
### `action` (string) Props
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
When `action` is a string, the `<Form>` component supports the following props:
|
||||
|
||||
| Prop | Example | Type | Required |
|
||||
| --------- | ------------------ | ------------------------------- | -------- |
|
||||
| `action` | `action="/search"` | `string` (URL or relative path) | Yes |
|
||||
| `replace` | `replace={false}` | `boolean` | - |
|
||||
| `scroll` | `scroll={true}` | `boolean` | - |
|
||||
|
||||
- **`action`**: The URL or path to navigate to when the form is submitted.
|
||||
- An empty string `""` will navigate to the same route with updated search params.
|
||||
- **`replace`**: Replaces the current history state instead of pushing a new one to the [browser's history](https://developer.mozilla.org/en-US/docs/Web/API/History_API) stack. Default is `false`.
|
||||
- **`scroll`**: Controls the scroll behavior during navigation. Defaults to `true`, this means it will scroll to the top of the new route, and maintain the scroll position for backwards and forwards navigation.
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
<AppOnly>
|
||||
|
||||
When `action` is a string, the `<Form>` component supports the following props:
|
||||
|
||||
| Prop | Example | Type | Required |
|
||||
| ---------- | ------------------ | ------------------------------- | -------- |
|
||||
| `action` | `action="/search"` | `string` (URL or relative path) | Yes |
|
||||
| `replace` | `replace={false}` | `boolean` | - |
|
||||
| `scroll` | `scroll={true}` | `boolean` | - |
|
||||
| `prefetch` | `prefetch={true}` | `boolean` | - |
|
||||
|
||||
- **`action`**: The URL or path to navigate to when the form is submitted.
|
||||
- An empty string `""` will navigate to the same route with updated search params.
|
||||
- **`replace`**: Replaces the current history state instead of pushing a new one to the [browser's history](https://developer.mozilla.org/en-US/docs/Web/API/History_API) stack. Default is `false`.
|
||||
- **`scroll`**: Controls the scroll behavior during navigation. Defaults to `true`, this means it will scroll to the top of the new route, and maintain the scroll position for backwards and forwards navigation.
|
||||
- **`prefetch`**: Controls whether the path should be prefetched when the form becomes visible in the user's viewport. Defaults to `true`.
|
||||
|
||||
### `action` (function) Props
|
||||
|
||||
When `action` is a function, the `<Form>` component supports the following prop:
|
||||
|
||||
| Prop | Example | Type | Required |
|
||||
| -------- | ------------------- | -------------------------- | -------- |
|
||||
| `action` | `action={myAction}` | `function` (Server Action) | Yes |
|
||||
|
||||
- **`action`**: The Server Action to be called when the form is submitted. See the [React docs](https://react.dev/reference/react-dom/components/form#props) for more.
|
||||
|
||||
> **Good to know**: When `action` is a function, the `replace` and `scroll` props are ignored.
|
||||
|
||||
</AppOnly>
|
||||
|
||||
### Caveats
|
||||
|
||||
<AppOnly>
|
||||
|
||||
- **`formAction`**: Can be used in a `<button>` or `<input type="submit">` fields to override the `action` prop. Next.js will perform a client-side navigation, however, this approach doesn't support prefetching.
|
||||
- When using [`basePath`](/docs/app/api-reference/config/next-config-js/basePath), you must also include it in the `formAction` path. e.g. `formAction="/base-path/search"`.
|
||||
- **`key`**: Passing a `key` prop to a string `action` is not supported. If you'd like to trigger a re-render or perform a mutation, consider using a function `action` instead.
|
||||
|
||||
</AppOnly>
|
||||
|
||||
- **`onSubmit`**: Can be used to handle form submission logic. However, calling `event.preventDefault()` will override `<Form>` behavior such as navigating to the specified URL.
|
||||
- **[`method`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method), [`encType`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#enctype), [`target`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#target)**: Are not supported as they override `<Form>` behavior.
|
||||
- Similarly, `formMethod`, `formEncType`, and `formTarget` can be used to override the `method`, `encType`, and `target` props respectively, and using them will fallback to native browser behavior.
|
||||
- If you need to use these props, use the HTML `<form>` element instead.
|
||||
- **`<input type="file">`**: Using this input type when the `action` is a string will match browser behavior by submitting the filename instead of the file object.
|
||||
|
||||
<AppOnly>
|
||||
|
||||
## Examples
|
||||
|
||||
### Search form that leads to a search result page
|
||||
|
||||
You can create a search form that navigates to a search results page by passing the path as an `action`:
|
||||
|
||||
```tsx filename="/app/page.tsx" switcher
|
||||
import Form from 'next/form'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
<input name="query" />
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/page.js" switcher
|
||||
import Form from 'next/form'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
<input name="query" />
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
When the user updates the query input field and submits the form, the form data will be encoded into the URL as search params, e.g. `/search?query=abc`.
|
||||
|
||||
> **Good to know**: If you pass an empty string `""` to `action`, the form will navigate to the same route with updated search params.
|
||||
|
||||
On the results page, you can access the query using the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) `page.js` prop and use it to fetch data from an external source.
|
||||
|
||||
```tsx filename="/app/search/page.tsx" switcher
|
||||
import { getSearchResults } from '@/lib/search'
|
||||
|
||||
export default async function SearchPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||||
}) {
|
||||
const results = await getSearchResults((await searchParams).query)
|
||||
|
||||
return <div>...</div>
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/search/page.js" switcher
|
||||
import { getSearchResults } from '@/lib/search'
|
||||
|
||||
export default async function SearchPage({ searchParams }) {
|
||||
const results = await getSearchResults((await searchParams).query)
|
||||
|
||||
return <div>...</div>
|
||||
}
|
||||
```
|
||||
|
||||
When the `<Form>` becomes visible in the user's viewport, shared UI (such as `layout.js` and `loading.js`) on the `/search` page will be prefetched. On submission, the form will immediately navigate to the new route and show loading UI while the results are being fetched. You can design the fallback UI using [`loading.js`](/docs/app/api-reference/file-conventions/loading):
|
||||
|
||||
```tsx filename="/app/search/loading.tsx" switcher
|
||||
export default function Loading() {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/search/loading.js" switcher
|
||||
export default function Loading() {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
```
|
||||
|
||||
To cover cases when shared UI hasn't yet loaded, you can show instant feedback to the user using [`useFormStatus`](https://react.dev/reference/react-dom/hooks/useFormStatus).
|
||||
|
||||
First, create a component that displays a loading state when the form is pending:
|
||||
|
||||
```tsx filename="/app/ui/search-button.tsx" switcher
|
||||
'use client'
|
||||
import { useFormStatus } from 'react-dom'
|
||||
|
||||
export default function SearchButton() {
|
||||
const status = useFormStatus()
|
||||
return (
|
||||
<button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/ui/search-button.js" switcher
|
||||
'use client'
|
||||
import { useFormStatus } from 'react-dom'
|
||||
|
||||
export default function SearchButton() {
|
||||
const status = useFormStatus()
|
||||
return (
|
||||
<button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Then, update the search form page to use the `SearchButton` component:
|
||||
|
||||
```tsx filename="/app/page.tsx" switcher
|
||||
import Form from 'next/form'
|
||||
import { SearchButton } from '@/ui/search-button'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
<input name="query" />
|
||||
<SearchButton />
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/ui/search-button.js" switcher
|
||||
import Form from 'next/form'
|
||||
import { SearchButton } from '@/ui/search-button'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action="/search">
|
||||
<input name="query" />
|
||||
<SearchButton />
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Mutations with Server Actions
|
||||
|
||||
You can perform mutations by passing a function to the `action` prop.
|
||||
|
||||
```tsx filename="/app/posts/create/page.tsx" switcher
|
||||
import Form from 'next/form'
|
||||
import { createPost } from '@/posts/actions'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action={createPost}>
|
||||
<input name="title" />
|
||||
{/* ... */}
|
||||
<button type="submit">Create Post</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/posts/create/page.js" switcher
|
||||
import Form from 'next/form'
|
||||
import { createPost } from '@/posts/actions'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Form action={createPost}>
|
||||
<input name="title" />
|
||||
{/* ... */}
|
||||
<button type="submit">Create Post</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
After a mutation, it's common to redirect to the new resource. You can use the [`redirect`](/docs/app/guides/redirecting) function from `next/navigation` to navigate to the new post page.
|
||||
|
||||
> **Good to know**: Since the "destination" of the form submission is not known until the action is executed, `<Form>` cannot automatically prefetch shared UI.
|
||||
|
||||
```tsx filename="/app/posts/actions.ts" switcher
|
||||
'use server'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export async function createPost(formData: FormData) {
|
||||
// Create a new post
|
||||
// ...
|
||||
|
||||
// Redirect to the new post
|
||||
redirect(`/posts/${data.id}`)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/posts/actions.js" switcher
|
||||
'use server'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export async function createPost(formData) {
|
||||
// Create a new post
|
||||
// ...
|
||||
|
||||
// Redirect to the new post
|
||||
redirect(`/posts/${data.id}`)
|
||||
}
|
||||
```
|
||||
|
||||
Then, in the new page, you can fetch data using the `params` prop:
|
||||
|
||||
```tsx filename="/app/posts/[id]/page.tsx" switcher
|
||||
import { getPost } from '@/posts/data'
|
||||
|
||||
export default async function PostPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>
|
||||
}) {
|
||||
const { id } = await params
|
||||
const data = await getPost(id)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{data.title}</h1>
|
||||
{/* ... */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="/app/posts/[id]/page.js" switcher
|
||||
import { getPost } from '@/posts/data'
|
||||
|
||||
export default async function PostPage({ params }) {
|
||||
const { id } = await params
|
||||
const data = await getPost(id)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{data.title}</h1>
|
||||
{/* ... */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See the [Server Actions](/docs/app/getting-started/mutating-data) docs for more examples.
|
||||
|
||||
</AppOnly>
|
||||
+1425
File diff suppressed because it is too large
Load Diff
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Components
|
||||
description: API Reference for Next.js built-in components.
|
||||
---
|
||||
|
||||
{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
|
||||
+1377
File diff suppressed because it is too large
Load Diff
+468
@@ -0,0 +1,468 @@
|
||||
---
|
||||
title: Script Component
|
||||
description: Optimize third-party scripts in your Next.js application using the built-in `next/script` Component.
|
||||
---
|
||||
|
||||
{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
|
||||
|
||||
This API reference will help you understand how to use [props](#props) available for the Script Component. For features and usage, please see the [Optimizing Scripts](/docs/app/guides/scripts) page.
|
||||
|
||||
```tsx filename="app/dashboard/page.tsx" switcher
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://example.com/script.js" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="app/dashboard/page.js" switcher
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://example.com/script.js" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
Here's a summary of the props available for the Script Component:
|
||||
|
||||
| Prop | Example | Type | Required |
|
||||
| ----------------------- | --------------------------------- | -------- | ------------------------------------- |
|
||||
| [`src`](#src) | `src="http://example.com/script"` | String | Required unless inline script is used |
|
||||
| [`strategy`](#strategy) | `strategy="lazyOnload"` | String | - |
|
||||
| [`onLoad`](#onload) | `onLoad={onLoadFunc}` | Function | - |
|
||||
| [`onReady`](#onready) | `onReady={onReadyFunc}` | Function | - |
|
||||
| [`onError`](#onerror) | `onError={onErrorFunc}` | Function | - |
|
||||
|
||||
## Required Props
|
||||
|
||||
The `<Script />` component requires the following properties.
|
||||
|
||||
### `src`
|
||||
|
||||
A path string specifying the URL of an external script. This can be either an absolute external URL or an internal path. The `src` property is required unless an inline script is used.
|
||||
|
||||
## Optional Props
|
||||
|
||||
The `<Script />` component accepts a number of additional properties beyond those which are required.
|
||||
|
||||
### `strategy`
|
||||
|
||||
The loading strategy of the script. There are four different strategies that can be used:
|
||||
|
||||
- `beforeInteractive`: Load before any Next.js code and before any page hydration occurs.
|
||||
- `afterInteractive`: (**default**) Load early but after some hydration on the page occurs.
|
||||
- `lazyOnload`: Load during browser idle time.
|
||||
- `worker`: (experimental) Load in a web worker.
|
||||
|
||||
### `beforeInteractive`
|
||||
|
||||
Scripts that load with the `beforeInteractive` strategy are injected into the initial HTML from the server, downloaded before any Next.js module, and executed in the order they are placed.
|
||||
|
||||
Scripts denoted with this strategy are preloaded and fetched before any first-party code, but their execution **does not block page hydration from occurring**.
|
||||
|
||||
<AppOnly>
|
||||
|
||||
`beforeInteractive` scripts must be placed inside the root layout (`app/layout.tsx`) and are designed to load scripts that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side).
|
||||
|
||||
</AppOnly>
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
`beforeInteractive` scripts must be placed inside the `Document` Component (`pages/_document.js`) and are designed to load scripts that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side).
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
**This strategy should only be used for critical scripts that need to be fetched as soon as possible.**
|
||||
|
||||
<AppOnly>
|
||||
|
||||
```tsx filename="app/layout.tsx" switcher
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
{children}
|
||||
<Script
|
||||
src="https://example.com/script.js"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="app/layout.js" switcher
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
{children}
|
||||
<Script
|
||||
src="https://example.com/script.js"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</AppOnly>
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
```jsx filename="pages/_document.js"
|
||||
import { Html, Head, Main, NextScript } from 'next/document'
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
<Script
|
||||
src="https://example.com/script.js"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
> **Good to know**: Scripts with `beforeInteractive` will always be injected inside the `head` of the HTML document regardless of where it's placed in the component.
|
||||
|
||||
Some examples of scripts that should be fetched as soon as possible with `beforeInteractive` include:
|
||||
|
||||
- Bot detectors
|
||||
- Cookie consent managers
|
||||
|
||||
### `afterInteractive`
|
||||
|
||||
Scripts that use the `afterInteractive` strategy are injected into the HTML client-side and will load after some (or all) hydration occurs on the page. **This is the default strategy** of the Script component and should be used for any script that needs to load as soon as possible but not before any first-party Next.js code.
|
||||
|
||||
`afterInteractive` scripts can be placed inside of any page or layout and will only load and execute when that page (or group of pages) is opened in the browser.
|
||||
|
||||
```jsx filename="app/page.js"
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://example.com/script.js" strategy="afterInteractive" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Some examples of scripts that are good candidates for `afterInteractive` include:
|
||||
|
||||
- Tag managers
|
||||
- Analytics
|
||||
|
||||
### `lazyOnload`
|
||||
|
||||
Scripts that use the `lazyOnload` strategy are injected into the HTML client-side during browser idle time and will load after all resources on the page have been fetched. This strategy should be used for any background or low priority scripts that do not need to load early.
|
||||
|
||||
`lazyOnload` scripts can be placed inside of any page or layout and will only load and execute when that page (or group of pages) is opened in the browser.
|
||||
|
||||
```jsx filename="app/page.js"
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://example.com/script.js" strategy="lazyOnload" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Examples of scripts that do not need to load immediately and can be fetched with `lazyOnload` include:
|
||||
|
||||
- Chat support plugins
|
||||
- Social media widgets
|
||||
|
||||
### `worker`
|
||||
|
||||
> **Warning:** The `worker` strategy is not yet stable and does not yet work with the App Router. Use with caution.
|
||||
|
||||
Scripts that use the `worker` strategy are off-loaded to a web worker in order to free up the main thread and ensure that only critical, first-party resources are processed on it. While this strategy can be used for any script, it is an advanced use case that is not guaranteed to support all third-party scripts.
|
||||
|
||||
To use `worker` as a strategy, the `nextScriptWorkers` flag must be enabled in `next.config.js`:
|
||||
|
||||
```js filename="next.config.js"
|
||||
module.exports = {
|
||||
experimental: {
|
||||
nextScriptWorkers: true,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`worker` scripts can **only currently be used in the `pages/` directory**:
|
||||
|
||||
```tsx filename="pages/home.tsx" switcher
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://example.com/script.js" strategy="worker" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="pages/home.js" switcher
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://example.com/script.js" strategy="worker" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### `onLoad`
|
||||
|
||||
> **Warning:** `onLoad` does not yet work with Server Components and can only be used in Client Components. Further, `onLoad` can't be used with `beforeInteractive` – consider using `onReady` instead.
|
||||
|
||||
Some third-party scripts require users to run JavaScript code once after the script has finished loading in order to instantiate content or call a function. If you are loading a script with either `afterInteractive` or `lazyOnload` as a loading strategy, you can execute code after it has loaded using the `onLoad` property.
|
||||
|
||||
Here's an example of executing a lodash method only after the library has been loaded.
|
||||
|
||||
```tsx filename="app/page.tsx" switcher
|
||||
'use client'
|
||||
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"
|
||||
onLoad={() => {
|
||||
console.log(_.sample([1, 2, 3, 4]))
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="app/page.js" switcher
|
||||
'use client'
|
||||
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"
|
||||
onLoad={() => {
|
||||
console.log(_.sample([1, 2, 3, 4]))
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### `onReady`
|
||||
|
||||
> **Warning:** `onReady` does not yet work with Server Components and can only be used in Client Components.
|
||||
|
||||
Some third-party scripts require users to run JavaScript code after the script has finished loading and every time the component is mounted (after a route navigation for example). You can execute code after the script's load event when it first loads and then after every subsequent component re-mount using the `onReady` property.
|
||||
|
||||
Here's an example of how to re-instantiate a Google Maps JS embed every time the component is mounted:
|
||||
|
||||
<AppOnly>
|
||||
|
||||
```tsx filename="app/page.tsx" switcher
|
||||
'use client'
|
||||
|
||||
import { useRef } from 'react'
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
const mapRef = useRef()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={mapRef}></div>
|
||||
<Script
|
||||
id="google-maps"
|
||||
src="https://maps.googleapis.com/maps/api/js"
|
||||
onReady={() => {
|
||||
new google.maps.Map(mapRef.current, {
|
||||
center: { lat: -34.397, lng: 150.644 },
|
||||
zoom: 8,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="app/page.js" switcher
|
||||
'use client'
|
||||
|
||||
import { useRef } from 'react'
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
const mapRef = useRef()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={mapRef}></div>
|
||||
<Script
|
||||
id="google-maps"
|
||||
src="https://maps.googleapis.com/maps/api/js"
|
||||
onReady={() => {
|
||||
new google.maps.Map(mapRef.current, {
|
||||
center: { lat: -34.397, lng: 150.644 },
|
||||
zoom: 8,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</AppOnly>
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
```jsx
|
||||
import { useRef } from 'react'
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
const mapRef = useRef()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={mapRef}></div>
|
||||
<Script
|
||||
id="google-maps"
|
||||
src="https://maps.googleapis.com/maps/api/js"
|
||||
onReady={() => {
|
||||
new google.maps.Map(mapRef.current, {
|
||||
center: { lat: -34.397, lng: 150.644 },
|
||||
zoom: 8,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
### `onError`
|
||||
|
||||
> **Warning:** `onError` does not yet work with Server Components and can only be used in Client Components. `onError` cannot be used with the `beforeInteractive` loading strategy.
|
||||
|
||||
Sometimes it is helpful to catch when a script fails to load. These errors can be handled with the `onError` property:
|
||||
|
||||
<AppOnly>
|
||||
|
||||
```tsx filename="app/page.tsx" switcher
|
||||
'use client'
|
||||
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="https://example.com/script.js"
|
||||
onError={(e: Error) => {
|
||||
console.error('Script failed to load', e)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx filename="app/page.js" switcher
|
||||
'use client'
|
||||
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="https://example.com/script.js"
|
||||
onError={(e) => {
|
||||
console.error('Script failed to load', e)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</AppOnly>
|
||||
|
||||
<PagesOnly>
|
||||
|
||||
```jsx
|
||||
import Script from 'next/script'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="https://example.com/script.js"
|
||||
onError={(e: Error) => {
|
||||
console.error('Script failed to load', e)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</PagesOnly>
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Changes |
|
||||
| --------- | ------------------------------------------------------------------------- |
|
||||
| `v13.0.0` | `beforeInteractive` and `afterInteractive` is modified to support `app`. |
|
||||
| `v12.2.4` | `onReady` prop added. |
|
||||
| `v12.2.2` | Allow `next/script` with `beforeInteractive` to be placed in `_document`. |
|
||||
| `v11.0.0` | `next/script` introduced. |
|
||||
Reference in New Issue
Block a user