Relevant source files
The following files were used as context for generating this wiki page:
- packages/third-parties/src/google/ga.tsx
- packages/third-parties/src/google/gtm.tsx
- packages/next/src/pages/_document.tsx
- packages/third-parties/src/google/index.tsx
- packages/next/src/client/script.tsx
- packages/third-parties/src/ThirdPartyScriptEmbed.tsx
- packages/third-parties/src/google/youtube-embed.tsx
- packages/third-parties/src/google/google-maps-embed.tsx
- packages/next/src/client/route-loader.ts
- packages/next/src/client/legacy/image.tsx
- packages/next/src/client/next-turbopack.ts
- packages/next/src/server/app-render/dynamic-rendering.ts
- packages/next/src/client/image-component.tsx
- packages/third-parties/package.json
- packages/third-parties/src/types/google.ts
- packages/eslint-plugin-next/src/rules/next-script-for-ga.ts
- packages/next/src/build/webpack-config.ts
- packages/next/font/google/index.js
- packages/next/src/server/request/cache-stages.ts
- packages/next/cache.js
- packages/next/src/server/config.ts
- packages/next/src/client/app-globals.ts
- packages/next/src/client/components/segment-cache/navigation-testing-lock.ts
- packages/next/src/client/components/links.ts
- packages/next/src/server/client-component-renderer-logger.ts
- packages/next/script.js
- packages/next/src/server/app-render/rsc/preloads.ts
- packages/next/src/lib/metadata/metadata-parallel.tsx
- packages/next/src/client/index.tsx
- packages/font/google/index.js
Overview
The @next/third-parties package provides optimized React components and utility functions for popular third-party libraries and services in Next.js applications. By integrating directly with Next.js script loading lifecycles and performance measurement APIs, it improves loading performance and simplifies the setup of third-party tools such as Google Analytics, Google Tag Manager, YouTube embeds, and Google Maps.
Sources: packages/third-parties/package.json:1-37, packages/third-parties/src/google/ga.tsx:1-57, packages/third-parties/src/google/gtm.tsx:1-75, packages/third-parties/src/google/youtube-embed.tsx:1-35, packages/third-parties/src/google/google-maps-embed.tsx:1-18
Package architecture and export surface
Package exports and module definitions
The @next/third-parties package (packages/third-parties) defines its public API via package manifest exports targeting ./google. The distribution files are generated using TypeScript (tsc) and placed in the dist directory, with published distribution contents including both compiled outputs and the google.d.ts type definitions.
Sources: packages/third-parties/package.json:1-37
The package exposes Google-related integrations through a single entry point mapped in package.json:
"exports": {
"./google": {
"types": "./dist/google/index.d.ts",
"default": "./dist/google/index.js"
}
},Sources: packages/third-parties/package.json:8-13
The entry point file packages/third-parties/src/google/index.tsx re-exports four core components and utility functions:
export { default as GoogleMapsEmbed } from './google-maps-embed'
export { default as YouTubeEmbed } from './youtube-embed'
export { GoogleTagManager, sendGTMEvent } from './gtm'
export { GoogleAnalytics, sendGAEvent } from './ga'Sources: packages/third-parties/src/google/index.tsx:1-4
Dependencies and peer constraints
The package relies on third-party-capital for third-party script rendering logic and enforces peer dependencies across Next.js and React versions.
| Dependency Type | Package Name | Version Specifier / Range |
|---|---|---|
| Dependency | third-party-capital | 1.0.20 |
| Dev Dependency | next | workspace:* |
| Peer Dependency | next | ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 |
| Peer Dependency | react | ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 |
Sources: packages/third-parties/package.json:25-37
Note
Global window extensions in packages/third-parties/src/types/google.ts augment the global Window interface with an optional dataLayer array of objects and dynamic index properties to support analytics event dispatching.
Sources: packages/third-parties/src/types/google.ts:1-6
Script loader integration and strategy
Execution Strategies and Component Lifecycle
The core script component defined in packages/next/src/client/script.tsx supports multiple execution strategies for third-party scripts. The entry point packages/next/script.js proxies directly to this client module export.
function Script(props: ScriptProps): JSX.Element | null {
const {
id,
src = '',
onLoad = () => {},
onReady = null,
strategy = 'afterInteractive',
onError,
stylesheets,
...restProps
} = propsSources: packages/next/src/client/script.tsx:200-210
The component relies on two refs (hasOnReadyEffectCalled and hasLoadScriptEffectCalled) to ensure that onLoad, onReady, and script injection callbacks execute precisely once across initial mounts and strict mode re-runs or <OffScreen /> boundary toggles.
const hasOnReadyEffectCalled = useRef(false)
useEffect(() => {
const cacheKey = id || src
if (!hasOnReadyEffectCalled.current) {
// Run onReady if script has loaded before but component is re-mounted
if (onReady && cacheKey && LoadCache.has(cacheKey)) {
onReady()
}
hasOnReadyEffectCalled.current = true
}
}, [onReady, id, src])
const hasLoadScriptEffectCalled = useRef(false)
useEffect(() => {
if (!hasLoadScriptEffectCalled.current) {
if (strategy === 'afterInteractive') {
loadScript(props)
} else if (strategy === 'lazyOnload') {
loadLazyScript(props)
}
hasLoadScriptEffectCalled.current = true
}
}, [props, strategy])Sources: packages/next/src/client/script.tsx:245-271
App Directory and Preloading Integration
When operating inside the App Directory (appDir), the component leverages ReactDOM.preinit for stylesheets and ReactDOM.preload for scripts. Before-interactive scripts push serialized payload configurations into a global queue self.__next_s instead of relying on native defer attributes.
// Before interactive scripts need to be loaded by Next.js' runtime instead
// of native <script> tags, because they no longer have `defer`.
if (strategy === 'beforeInteractive') {
if (!src) {
// For inlined scripts, we put the content in `children`.
if (restProps.dangerouslySetInnerHTML) {
// Casting since lib.dom.d.ts doesn't have TrustedHTML yet.
restProps.children = restProps.dangerouslySetInnerHTML
.__html as string
delete restProps.dangerouslySetInnerHTML
}
return (
<script
nonce={nonce}
dangerouslySetInnerHTML={{
__html: `(self.__next_s=self.__next_s||[]).push(${htmlEscapeJsonString(
JSON.stringify([0, { ...restProps, id }])
)})`,
}}
/>
)
} else {
// @ts-ignore
ReactDOM.preload(
src,
restProps.integrity
? {
as: 'script',
integrity: restProps.integrity,
nonce,
crossOrigin: restProps.crossOrigin,
}
: { as: 'script', nonce, crossOrigin: restProps.crossOrigin }
)
return (
<script
nonce={nonce}
dangerouslySetInnerHTML={{
__html: `(self.__next_s=self.__next_s||[]).push(${htmlEscapeJsonString(
JSON.stringify([src, { ...restProps, id }])
)})`,
}}
/>
)
}Sources: packages/next/src/client/script.tsx:315-360
Note
beforeInteractive scripts without a src attribute take their contents from dangerouslySetInnerHTML.__html, assigning it to restProps.children after sanitizing the markup structure.
Sources: packages/next/src/client/script.tsx:318-325
Google Analytics and Tag Manager
Client Component Implementation and DataLayer Initialization
The GoogleAnalytics and GoogleTagManager components are implemented as Client Components, indicated by the 'use client' directive. Both components emit a feature usage telemetry signal via performance.mark('mark_feature_usage', { detail: { feature: 'next-third-parties-ga' } }) or next-third-parties-gtm inside an useEffect hook upon mounting.
Sources: packages/third-parties/src/google/ga.tsx:1-34, packages/third-parties/src/google/gtm.tsx:1-51
GoogleAnalytics accepts gaId, optional dataLayerName (defaulting to 'dataLayer'), a boolean debugMode, and a nonce. It renders two Script elements: an inline initialization script (_next-ga-init) that establishes the global array queue and defines the gtag() helper function, followed by the remote script (_next-ga) loading https://www.googletagmanager.com/gtag/js?id=${gaId}.
export function GoogleAnalytics(props: GAParams) {
const { gaId, debugMode, dataLayerName = 'dataLayer', nonce } = props
if (currDataLayerName === undefined) {
currDataLayerName = dataLayerName
}
useEffect(() => {
// performance.mark is being used as a feature use signal. While it is traditionally used for performance
// benchmarking it is low overhead and thus considered safe to use in production and it is a widely available
// existing API.
// The performance measurement will be handled by Chrome Aurora
performance.mark('mark_feature_usage', {
detail: {
feature: 'next-third-parties-ga',
},
})
}, [])
return (
<>
<Script
id="_next-ga-init"
dangerouslySetInnerHTML={{
__html: `
window['${dataLayerName}'] = window['${dataLayerName}'] || [];
function gtag(){window['${dataLayerName}'].push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}' ${debugMode ? ",{ 'debug_mode': true }" : ''});`,
}}
nonce={nonce}
/>
<Script
id="_next-ga"
src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`}
nonce={nonce}
/>
</>
)Sources: packages/third-parties/src/google/ga.tsx:16-56
GoogleTagManager constructs a dynamic script URL defaulting to https://www.googletagmanager.com/gtm.js, appending URL query parameters based on its props.
| Prop Name | Query Parameter | Condition / Behavior |
|---|---|---|
gtmId | id | Appended when gtmId is provided |
dataLayerName | l | Appended when dataLayerName is not equal to 'dataLayer' |
auth | gtm_auth | Appended when auth string is provided |
preview | gtm_preview, gtm_cookies_win | Appended when preview string is provided (sets gtm_cookies_win to 'x') |
Sources: packages/third-parties/src/google/gtm.tsx:10-39
Note
The GoogleTagManager initialization inline script creates an IIFE that safely instantiates the data layer array, pushes a gtm.start timestamp event, and supports serializing an optional initial dataLayer object payload if passed via props.
Sources: packages/third-parties/src/google/gtm.tsx:55-66
Event Dispatching and Window DataLayer Management
Both integrations expose helper functions to dispatch tracking events into the global dataLayer queue: sendGAEvent and sendGTMEvent.
sendGAEvent verifies whether currDataLayerName has been initialized. If undefined, it emits a console warning. Otherwise, it pushes arguments onto window[currDataLayerName], warning if the data layer array does not exist on window.
export function sendGAEvent(..._args: Object[]) {
if (currDataLayerName === undefined) {
console.warn(`@next/third-parties: GA has not been initialized`)
return
}
if (window[currDataLayerName]) {
window[currDataLayerName].push(arguments)
} else {
console.warn(
`@next/third-parties: GA dataLayer ${currDataLayerName} does not exist`
)
}
}Sources: packages/third-parties/src/google/ga.tsx:59-72
sendGTMEvent accepts event data and an optional custom dataLayerName, falling back to currDataLayerName. It defensively initializes window[dataLayer] as an empty array if absent, allowing event queueing prior to full GTM script initialization.
export const sendGTMEvent = (data: Object, dataLayerName?: string) => {
// special case if we are sending events before GTM init and we have custom dataLayerName
const dataLayer = dataLayerName || currDataLayerName
// define dataLayer so we can still queue up events before GTM init
window[dataLayer] = window[dataLayer] || []
window[dataLayer].push(data)
}Sources: packages/third-parties/src/google/gtm.tsx:77-83
Warning
Using standard inline script tags (<script src="www.googletagmanager.com/gtag/js"> or www.google-analytics.com/analytics.js) directly in React code will trigger the ESLint rule next-script-for-ga, which requires replacing them with @next/third-parties/google components.
Sources: packages/eslint-plugin-next/src/rules/next-script-for-ga.ts:10-54
Shared script embed wrapper
Core Component Structure and Type Definitions
ThirdPartyScriptEmbed is a client-side React component marked with the 'use client' directive, serving as the foundational container wrapper for embedded third-party widgets and scripts across @next/third-parties. The component accepts a strongly typed ScriptEmbed configuration object defining properties for raw HTML injection, sizing dimensions, element children, and telemetry feature tracking.
export type ScriptEmbed = {
html?: string | null
height?: string | number | null
width?: string | number | null
children?: React.ReactElement | React.ReactElement[]
dataNtpc?: string
}Sources: packages/third-parties/src/ThirdPartyScriptEmbed.tsx:1-11
The following table details the configuration parameters accepted by ThirdPartyScriptEmbed:
| Property | Type | Default Value | Description |
|---|---|---|---|
html | string | null | undefined | Raw HTML string to be injected via dangerouslySetInnerHTML. |
height | string | number | null | null | Container height; appends px if numeric, or evaluates to auto if null. |
width | string | number | null | null | Container width; appends px if numeric, or evaluates to auto if null. |
children | React.ReactElement | React.ReactElement[] | undefined | React child elements rendered alongside the container. |
dataNtpc | string | '' | Identifier string appended to feature telemetry and data attributes. |
Sources: packages/third-parties/src/ThirdPartyScriptEmbed.tsx:5-19
Telemetry Signal Emission and DOM Rendering
Upon mounting, ThirdPartyScriptEmbed executes an useEffect hook that inspects the dataNtpc prop. If dataNtpc is truthy, the component invokes performance.mark() with the feature usage signal name formatted as next-third-parties-${dataNtpc}. This low-overhead browser performance API is leveraged as a safe production telemetry signal to track feature adoption.
useEffect(() => {
if (dataNtpc) {
// performance.mark is being used as a feature use signal. While it is traditionally used for performance
// benchmarking it is low overhead and thus considered safe to use in production and it is a widely available
// existing API.
performance.mark('mark_feature_usage', {
detail: {
feature: `next-third-parties-${dataNtpc}`,
},
})
}
}, [dataNtpc])Sources: packages/third-parties/src/ThirdPartyScriptEmbed.tsx:20-31
Note
performance.mark is deliberately utilized for feature usage signaling due to its negligible execution overhead, making it safe for production monitoring without triggering custom analytics overhead.
Sources: packages/third-parties/src/ThirdPartyScriptEmbed.tsx:21-24
The component returns a React fragment containing any provided children elements. If the html prop is present, it renders a wrapper div element configured with dynamic inline dimensions and a data-ntpc attribute matching the dataNtpc prop.
return (
<>
{/* insert script children */}
{children}
{/* insert html */}
{html ? (
<div
style={{
height: height != null ? `${height}px` : 'auto',
width: width != null ? `${width}px` : 'auto',
}}
data-ntpc={dataNtpc}
dangerouslySetInnerHTML={{ __html: html }}
/>
) : null}
</>
)Sources: packages/third-parties/src/ThirdPartyScriptEmbed.tsx:33-49
YouTube and Google Maps embeds
YouTube Embed Mechanics and Strategy
The YouTubeEmbed component relies on third-party-capital via TPCYouTubeEmbed to resolve raw markup, external stylesheets, and script configurations for video embeds based on standard parameters such as videoid, dimensions, and custom player options.
export type YouTubeEmbed = {
height?: number
width?: number
videoid: string
playlabel?: string
params?: string
style?: string
}Sources: packages/third-parties/src/types/google.ts:59-65
YouTubeEmbed delegates its wrapper rendering to ThirdPartyScriptEmbed, stamping the container with a dataNtpc value of "YouTubeEmbed". The returned element maps over any required external scripts provided by third-party-capital, instantiating Next.js Script components with execution strategies mapped explicitly through scriptStrategy.
const scriptStrategy = {
server: 'beforeInteractive',
client: 'afterInteractive',
idle: 'lazyOnload',
worker: 'worker',
}Sources: packages/third-parties/src/google/youtube-embed.tsx:9-14
export default function YouTubeEmbed(props: YouTubeEmbedTypes) {
const { html, scripts, stylesheets } = TPCYouTubeEmbed(props)
return (
<ThirdPartyScriptEmbed
height={props.height || null}
width={props.width || null}
html={html}
dataNtpc="YouTubeEmbed"
>
{scripts?.map((script) => (
<Script
key={script.url}
src={script.url}
strategy={scriptStrategy[script.strategy] as ScriptProps['strategy']}
stylesheets={stylesheets}
/>
))}
</ThirdPartyScriptEmbed>
)Sources: packages/third-parties/src/google/youtube-embed.tsx:16-35
Note
The scriptStrategy lookup maps third-party-capital script descriptor strings directly to Next.js Script component loading strategies, routing idle descriptors to lazyOnload and server descriptors to beforeInteractive.
Sources: packages/third-parties/src/google/youtube-embed.tsx:9-14
Google Maps Embed Integration
The GoogleMapsEmbed component accepts location and display configuration properties, extracting the apiKey property and formatting it as a standard key parameter before passing the object to TPCGoogleMapEmbed.
export type GoogleMapsEmbed = {
height?: number | string
width?: number | string
mode: 'place' | 'view' | 'directions' | 'streetview' | 'search'
apiKey: string
style?: string
allowfullscreen?: boolean
loading?: 'eager' | 'lazy'
q?: string
id?: string
center?: string
zoom?: string
maptype?: string
language?: string
region?: string
}Sources: packages/third-parties/src/types/google.ts:42-57
export default function GoogleMapsEmbed(props: GoogleMapsEmbedTypes) {
const { apiKey, ...restProps } = props
const formattedProps = { ...restProps, key: apiKey }
const { html } = TPCGoogleMapEmbed(formattedProps)
return (
<ThirdPartyScriptEmbed
height={formattedProps.height || null}
width={formattedProps.width || null}
html={html}
dataNtpc="GoogleMapsEmbed"
></ThirdPartyScriptEmbed>
)Sources: packages/third-parties/src/google/google-maps-embed.tsx:6-18
Warning
Omitting the apiKey property or supplying an invalid mode will result in malformed output generation from the underlying third-party-capital mapper function.
Sources: packages/third-parties/src/google/google-maps-embed.tsx:7-9
Related
On This Page
OverviewPackage architecture and export surfacePackage exports and module definitionsDependencies and peer constraintsScript loader integration and strategyExecution Strategies and Component LifecycleApp Directory and Preloading IntegrationGoogle Analytics and Tag ManagerClient Component Implementation and DataLayer InitializationEvent Dispatching and Window DataLayer ManagementShared script embed wrapperCore Component Structure and Type DefinitionsTelemetry Signal Emission and DOM RenderingYouTube and Google Maps embedsYouTube Embed Mechanics and StrategyGoogle Maps Embed IntegrationRelated