- Next.js
- Asset Optimization & Performance
- Third-Party Script Optimization with next/script
Third-Party Script Optimization with next/script
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
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.tsx2import Script from 'next/script';34export default function RootLayout({ children }: { children: React.ReactNode }) {5 return (6 <html lang="en">7 <body>8 {children}910 {/*11 1. afterInteractive (Default):12 Loads immediately after the page hydrates.13 Ideal for analytics/tag managers that should track initial views.14 */}15 <Script16 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 />2829 {/*30 2. lazyOnload:31 Waits until the browser is idle (requestIdleCallback).32 Ideal for non-critical chat widgets (Intercom, Drift).33 */}34 <Script35 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
What is the difference between onLoad and onReady?
`onLoad` fires once when the script finishes loading. `onReady` fires on initial load AND every time the component re-mounts after client navigation.
Can next/script be used with inline scripts?
Yes! Inline scripts require an explicit `id` prop for Next.js to track and deduplicate them.
