Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cloudflare/vinext/llms.txt
Use this file to discover all available pages before exploring further.
The Script component provides optimized loading strategies for third-party scripts (analytics, ads, widgets) with control over when and how they execute.
Import
import Script from 'next/script'
Basic Usage
import Script from 'next/script'
export default function Page() {
return (
<>
<Script src="https://example.com/script.js" />
<div>Page content</div>
</>
)
}
API Reference
Props
Script source URL.<Script src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID" />
strategy
'beforeInteractive' | 'afterInteractive' | 'lazyOnload' | 'worker'
default:"'afterInteractive'"
Loading strategy that controls when the script executes.
beforeInteractive — Load before page becomes interactive (SSR output)
afterInteractive — Load after page becomes interactive (default)
lazyOnload — Load during idle time (after window.load)
worker — Load in a web worker (requires Partytown)
<Script src="/critical.js" strategy="beforeInteractive" />
<Script src="/analytics.js" strategy="afterInteractive" />
<Script src="/ads.js" strategy="lazyOnload" />
<Script src="/heavy.js" strategy="worker" />
Callback invoked when the script has loaded.<Script
src="https://example.com/sdk.js"
onLoad={() => {
console.log('Script loaded')
window.ExampleSDK.init()
}}
/>
Callback invoked when the script is ready.Called after onLoad, and also on every re-render if the script is already loaded.<Script
src="https://maps.googleapis.com/maps/api/js"
onReady={() => {
new google.maps.Map(document.getElementById('map'))
}}
/>
Callback invoked when the script fails to load.<Script
src="https://example.com/script.js"
onError={(e) => {
console.error('Script failed to load', e)
}}
/>
Unique identifier for the script (prevents duplicate loading).<Script id="google-analytics" src="https://www.googletagmanager.com/gtag/js" />
Inline script content.<Script id="inline-script">
{`console.log('Hello from inline script')`}
</Script>
Alternative to children for inline scripts.<Script
id="config"
dangerouslySetInnerHTML={{
__html: `window.CONFIG = ${JSON.stringify(config)}`
}}
/>
Standard Script Attributes
All standard <script> attributes are supported:
Script MIME type (e.g., "module", "text/partytown").
Load script asynchronously.
CORS setting ("anonymous" or "use-credentials").
Nonce for Content Security Policy.
Subresource Integrity hash.
Loading Strategies
beforeInteractive
Load the script before the page becomes interactive. Rendered in SSR output.
Use for:
- Critical polyfills
- Feature detection scripts
- Scripts that must run before React hydration
import Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html>
<body>
<Script
src="/polyfills.js"
strategy="beforeInteractive"
/>
{children}
</body>
</html>
)
}
Only works in app/layout.tsx or pages/_document.tsx. Ignored elsewhere.
afterInteractive (Default)
Load the script after the page becomes interactive.
Use for:
- Analytics scripts
- Tag managers
- Chat widgets
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
<div>Page content</div>
</>
)
}
lazyOnload
Load the script during idle time (after window.load + requestIdleCallback).
Use for:
- Non-critical scripts
- Ads
- Social media widgets
- Anything that can wait
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() => console.log('Facebook SDK loaded')}
/>
<div>Page content</div>
</>
)
}
worker (Partytown)
Load the script in a web worker using Partytown.
Use for:
- Heavy analytics scripts
- Anything that blocks the main thread
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="worker"
/>
<div>Page content</div>
</>
)
}
Requires Partytown setup. Sets type="text/partytown" on the script tag.
Examples
Google Analytics
import Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html>
<body>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_ID}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_GA_ID}');
`}
</Script>
{children}
</body>
</html>
)
}
Facebook Pixel
import Script from 'next/script'
export default function Page() {
return (
<>
<Script id="facebook-pixel" strategy="afterInteractive">
{`
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '${process.env.NEXT_PUBLIC_FB_PIXEL_ID}');
fbq('track', 'PageView');
`}
</Script>
<noscript>
<img
height="1"
width="1"
style={{ display: 'none' }}
src={`https://www.facebook.com/tr?id=${process.env.NEXT_PUBLIC_FB_PIXEL_ID}&ev=PageView&noscript=1`}
/>
</noscript>
<div>Page content</div>
</>
)
}
External SDK
import Script from 'next/script'
import { useState } from 'react'
export default function Page() {
const [sdkReady, setSdkReady] = useState(false)
return (
<>
<Script
src="https://sdk.example.com/v1/sdk.js"
strategy="afterInteractive"
onLoad={() => {
window.ExampleSDK.init({ apiKey: process.env.NEXT_PUBLIC_API_KEY })
setSdkReady(true)
}}
onError={() => {
console.error('Failed to load Example SDK')
}}
/>
{sdkReady ? (
<button onClick={() => window.ExampleSDK.doSomething()}>
Use SDK
</button>
) : (
<div>Loading SDK...</div>
)}
</>
)
}
Inline Configuration
import Script from 'next/script'
export default function Page() {
const config = {
apiUrl: process.env.NEXT_PUBLIC_API_URL,
features: ['feature1', 'feature2'],
}
return (
<>
<Script
id="app-config"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{
__html: `window.APP_CONFIG = ${JSON.stringify(config)}`
}}
/>
<div>Page content</div>
</>
)
}
Deduplication
Scripts with the same id or src are only loaded once:
// Both components render the same script — only loaded once
function ComponentA() {
return <Script id="analytics" src="/analytics.js" />
}
function ComponentB() {
return <Script id="analytics" src="/analytics.js" />
}
SSR Behavior
beforeInteractive: Rendered in SSR HTML output
afterInteractive: Not rendered (injected client-side)
lazyOnload: Not rendered (injected client-side)
worker: Not rendered (injected client-side)
Impact on Core Web Vitals
| Strategy | FCP | LCP | CLS | TBT |
|---|
beforeInteractive | 🟡 | 🟡 | ✅ | 🟡 |
afterInteractive | ✅ | ✅ | ✅ | 🟡 |
lazyOnload | ✅ | ✅ | ✅ | ✅ |
worker | ✅ | ✅ | ✅ | ✅ |
Best Practices
- Use
lazyOnload for non-critical scripts
- Minimize
beforeInteractive usage (blocks hydration)
- Use
worker for heavy analytics (requires Partytown)
- Always provide
id for inline scripts (enables deduplication)
- Avoid blocking the main thread
Limitations
beforeInteractive placement: Only works in app/layout.tsx or pages/_document.tsx. Silently ignored in page components.
No SSR execution: Inline scripts do not execute during SSR. Use them for browser-only code.
CSP nonces: If using CSP, pass nonce to all inline scripts.
Migration from HTML script tag
| HTML | Next.js Script |
|---|
<script src="..."> | <Script src="..." /> |
<script async src="..."> | <Script src="..." strategy="afterInteractive" /> |
<script defer src="..."> | <Script src="..." strategy="afterInteractive" /> |
<script>...</script> (in head) | <Script id="..." strategy="beforeInteractive">...</Script> |
<script>...</script> (in body) | <Script id="...">...</Script> |
Source
View source code →
Implementation: /home/daytona/workspace/source/packages/vinext/src/shims/script.tsx