Skip to main content
The <Script> component gives you control over when third-party scripts load, helping you improve performance without manually managing <script> tag placement.
app/dashboard/page.js
import Script from 'next/script'

export default function Dashboard() {
  return (
    <>
      <Script src="https://example.com/script.js" />
    </>
  )
}

Props

src
string
required
The URL of the external script to load. Required unless you are using an inline script.
<Script src="https://example.com/script.js" />
strategy
string
default:"afterInteractive"
Controls when the script loads. Four strategies are available:
  • "beforeInteractive" — loads before any Next.js code and before page hydration
  • "afterInteractive" — (default) loads early, after some hydration has occurred
  • "lazyOnload" — loads during browser idle time, after all other resources
  • "worker" — (experimental) offloads the script to a web worker
onLoad
function
Called once after the script finishes loading. Use to run initialization code that depends on the script.Only works with afterInteractive and lazyOnload strategies. Requires a Client Component.
'use client'

<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
function
Called after the script loads and on every subsequent component mount (for example, after client-side navigation). Useful for scripts that need to reinitialize on navigation.Requires a Client Component.
'use client'

import { useRef } from 'react'
import Script from 'next/script'

export default function Page() {
  const mapRef = useRef()
  return (
    <>
      <div ref={mapRef} />
      <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,
          })
        }}
      />
    </>
  )
}
onError
function
Called if the script fails to load. Cannot be used with beforeInteractive. Requires a Client Component.
'use client'

<Script
  src="https://example.com/script.js"
  onError={(e) => console.error('Script failed to load', e)}
/>

Loading strategies

beforeInteractive

Injects the script into the initial HTML from the server. The script downloads before any Next.js module but does not block hydration. Must be placed in the root layout (app/layout.js). Use only for critical scripts needed site-wide, such as bot detectors and cookie consent managers.
app/layout.js
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>
  )
}
Scripts with beforeInteractive are always injected into the <head> regardless of their placement in the component tree.

afterInteractive

The default strategy. Injects the script client-side after some hydration has occurred. Good for tag managers and analytics.
app/page.js
import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="afterInteractive" />
    </>
  )
}

lazyOnload

Injects the script client-side during browser idle time, after all other resources have loaded. Use for low-priority scripts like chat widgets and social media embeds.
app/page.js
import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="lazyOnload" />
    </>
  )
}

worker (experimental)

The worker strategy is experimental and does not yet work with the App Router. Use with caution.
Offloads the script to a web worker to keep the main thread free for first-party code. Requires enabling nextScriptWorkers in next.config.js:
next.config.js
module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
}
Currently only supported in the pages/ directory:
pages/home.js
import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="worker" />
    </>
  )
}

Inline scripts

For inline scripts, use the id prop so Next.js can track and optimize the script:
<Script id="analytics-init">
  {`window.dataLayer = window.dataLayer || [];`}
</Script>
Or use dangerouslySetInnerHTML:
<Script
  id="show-banner"
  dangerouslySetInnerHTML={{
    __html: `document.getElementById('banner').classList.remove('hidden');`,
  }}
/>

Version history

VersionChanges
v13.0.0beforeInteractive and afterInteractive updated to support App Router
v12.2.4onReady prop added
v12.2.2beforeInteractive supported in _document
v11.0.0next/script introduced

Build docs developers (and LLMs) love