Next.js Curriculum
MediumAsset Optimization100% Free25 min read

Third-Party Script Optimization with next/script

The 30-Second Interview Answer

Third-party scripts (Google Tag Manager, analytics, chat widgets) are the #1 cause of degraded Total Blocking Time (TBT) and Interaction to Next Paint (INP). The next/script component optimizes execution timing via four strategies: beforeInteractive (critical polyfills before hydration), afterInteractive (default, after hydration), lazyOnload (idle browser time), and worker (experimental web worker offloading via Partytown; note: official docs state worker does not yet work in App Router, where @next/third-parties or lazyOnload is recommended).

Learning Objectives

Categorize third-party scripts into the appropriate next/script loading strategy.

Improve Interaction to Next Paint (INP) and Total Blocking Time (TBT).

Understand the capabilities and framework limitations of the experimental worker strategy.

Execute code after script load using onLoad and onReady callbacks.

Prerequisites & Background Knowledge
Browser main thread execution modelTotal Blocking Time (TBT)Interaction to Next Paint (INP)

Why Interviewers Care

Critical for real-world enterprise websites burdened with dozens of marketing tags and customer tracking scripts.

The Challenge

Compare the four loading strategies of next/script. Implement Google Tag Manager with afterInteractive and a customer support chat widget with lazyOnload.

Code Architecture & Implementation

1// app/layout.tsx
2import Script from 'next/script';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5 return (
6 <html lang="en">
7 <body>
8 {children}
9
10 {/*
11 1. afterInteractive (Default):
12 Loads immediately after the page hydrates.
13 Ideal for analytics/tag managers that should track initial views.
14 */}
15 <Script
16 id="google-tag-manager"
17 strategy="afterInteractive"
18 dangerouslySetInnerHTML={{
19 __html: `
20 (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
21 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
22 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
23 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
24 })(window,document,'script','dataLayer','GTM-XXXXXXX');
25 `,
26 }}
27 />
28
29 {/*
30 2. lazyOnload:
31 Waits until the browser is idle (requestIdleCallback).
32 Ideal for non-critical chat widgets (Intercom, Drift).
33 */}
34 <Script
35 src="https://widget.intercom.io/widget/app_id"
36 strategy="lazyOnload"
37 onLoad={() => {
38 console.log('Intercom chat widget loaded during browser idle time!');
39 }}
40 />
41 </body>
42 </html>
43 );
44}

Architectural Explanation

Native <script> tags block the browser HTML parser and compete with React hydration for main-thread CPU time, creating high Total Blocking Time (TBT). next/script manages script scheduling: beforeInteractive injects scripts into early HTML for essential security or bot detection; afterInteractive waits until React has hydrated the DOM; lazyOnload defers loading until all other page assets are idle; and worker historically moved execution off the main thread to Partytown (currently experimental and restricted to Pages Router; App Router relies on @next/third-parties and lazyOnload).

Common Mistakes & Anti-Patterns

Using regular `<script>` tags instead of `<Script>`, losing automatic deduplication and hydration coordination.

Loading heavy customer service chat widgets with `beforeInteractive`, ruining mobile performance scores.

Attempting to use strategy="worker" in the App Router without knowing that official Next.js documentation states it is not yet supported in the app/ directory.

Placing `beforeInteractive` scripts in sub-pages or leaf components (only allowed in root layout).

Interview Follow-Up Traps

The interviewer asks: "Can you offload Google Analytics to a Web Worker in Next.js App Router using strategy='worker'?" Trap Answer: "Yes, just set strategy='worker'." Correct Answer: "No! The official Next.js documentation explicitly states that strategy='worker' is experimental and does NOT yet work with the App Router. In modern App Router applications, you should use the official @next/third-parties library (e.g. GoogleTagManager) or strategy='lazyOnload' instead."

The interviewer asks: "What happens if two components render the same <Script src="...">?" Next.js tracks script sources and guarantees the script is loaded and executed only once in the DOM.

Frequently Asked Questions

Does strategy="worker" work with the Next.js App Router?

No. The official Next.js documentation explicitly cautions that strategy="worker" is experimental and does not yet work with the App Router (app/ directory). For App Router applications, use the official @next/third-parties package or strategy="lazyOnload".

`onLoad` fires once when the script finishes loading. `onReady` fires on initial load AND every time the component re-mounts after client navigation.

Yes! Inline scripts require an explicit `id` prop for Next.js to track and deduplicate them.