Files
stack/packages/mosaic/framework/skills/next-best-practices/scripts.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

3.6 KiB

Scripts

Loading third-party scripts in Next.js.

Use next/script

Always use next/script instead of native <script> tags for better performance.

// Bad: Native script tag
<script src="https://example.com/script.js"></script>;

// Good: Next.js Script component
import Script from 'next/script';

<Script src="https://example.com/script.js" />;

Inline Scripts Need ID

Inline scripts require an id attribute for Next.js to track them.

// Bad: Missing id
<Script dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />

// Good: Has id
<Script id="my-script" dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />

// Good: Inline with id
<Script id="show-banner">
  {`document.getElementById('banner').classList.remove('hidden')`}
</Script>

Don't Put Script in Head

next/script should not be placed inside next/head. It handles its own positioning.

// Bad: Script inside Head
import Head from 'next/head'
import Script from 'next/script'

<Head>
  <Script src="/analytics.js" />
</Head>

// Good: Script outside Head
<Head>
  <title>Page</title>
</Head>
<Script src="/analytics.js" />

Loading Strategies

// afterInteractive (default) - Load after page is interactive
<Script src="/analytics.js" strategy="afterInteractive" />

// lazyOnload - Load during idle time
<Script src="/widget.js" strategy="lazyOnload" />

// beforeInteractive - Load before page is interactive (use sparingly)
// Only works in app/layout.tsx or pages/_document.js
<Script src="/critical.js" strategy="beforeInteractive" />

// worker - Load in web worker (experimental)
<Script src="/heavy.js" strategy="worker" />

Google Analytics

Use @next/third-parties instead of inline GA scripts.

// Bad: Inline GA script
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX" />
<Script id="ga-init">
  {`window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', 'G-XXXXX');`}
</Script>

// Good: Next.js component
import { GoogleAnalytics } from '@next/third-parties/google'

export default function Layout({ children }) {
  return (
    <html>
      <body>{children}</body>
      <GoogleAnalytics gaId="G-XXXXX" />
    </html>
  )
}

Google Tag Manager

import { GoogleTagManager } from '@next/third-parties/google';

export default function Layout({ children }) {
  return (
    <html>
      <GoogleTagManager gtmId="GTM-XXXXX" />
      <body>{children}</body>
    </html>
  );
}

Other Third-Party Scripts

// YouTube embed
import { YouTubeEmbed } from '@next/third-parties/google';

<YouTubeEmbed videoid="dQw4w9WgXcQ" />;

// Google Maps
import { GoogleMapsEmbed } from '@next/third-parties/google';

<GoogleMapsEmbed apiKey="YOUR_API_KEY" mode="place" q="Brooklyn+Bridge,New+York,NY" />;

Quick Reference

Pattern Issue Fix
<script src="..."> No optimization Use next/script
<Script> without id Can't track inline scripts Add id attribute
<Script> inside <Head> Wrong placement Move outside Head
Inline GA/GTM scripts No optimization Use @next/third-parties
strategy="beforeInteractive" outside layout Won't work Only use in root layout