NextjsScript Optimization (next/script)

Script Optimization (next/script)

Third-party scripts — analytics, ads, chat widgets, A/B testing tools — are notorious for slowing pages down. A naive <script> tag dropped into the page can block HTML parsing while it downloads and executes, delaying everything else from rendering. The <Script> component from next/script gives you explicit control over when a script loads relative to the rest of the page.
Warning
A plain <script src="..."> tag placed in the <head> or body without any async/defer attribute is render-blocking by default — the browser pauses parsing the rest of the HTML until that script finishes downloading and running. On a slow third-party server, this alone can add seconds to the time before anything appears on screen.
The strategy prop
<Script> accepts a strategy prop that tells Next.js exactly when to load and execute the script.

Strategy

Loads

Typical use case

beforeInteractive

Before any page JavaScript, injected into the initial HTML

Bot detection, consent management — scripts that must run before the page is interactive

afterInteractive (default)

Early on, right after the page becomes interactive

Most analytics tools, tag managers

lazyOnload

During idle time, after everything else has loaded

Chat widgets, social share buttons — low-priority scripts

A worked analytics example

app/layout.tsx

TSX
import Script from 'next/script'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {children}

        <Script
          src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
          strategy="afterInteractive"
        />
        <Script id="ga-init" strategy="afterInteractive">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', 'GA_MEASUREMENT_ID');
          `}
        </Script>
      </body>
    </html>
  )
}
Notice the second <Script> has no src — it wraps an inline script instead, which is exactly how you initialize a library after its main bundle has loaded. Giving inline scripts a unique id is required so Next.js can track and dedupe them correctly.
Note
Scripts with strategy="beforeInteractive" should be placed in the root layout, and Next.js automatically injects them into the document's <head> regardless of where in the component tree they're written — this strategy is reserved for scripts your page truly cannot function without.
onLoad and onError

TSX
'use client'

import Script from 'next/script'

export default function ChatWidget() {
  return (
    <Script
      src="https://widget.example.com/chat.js"
      strategy="lazyOnload"
      onLoad={() => console.log('Chat widget loaded')}
      onError={(e) => console.error('Failed to load chat widget', e)}
    />
  )
}
  • <Script> replaces plain <script> tags to control exactly when a third-party script loads relative to page rendering.

  • strategy="beforeInteractive" loads before any page JS — reserve it for scripts the page cannot function without.

  • strategy="afterInteractive" (the default) suits most analytics and tag manager scripts.

  • strategy="lazyOnload" defers low-priority scripts like chat widgets until the browser is idle.