diff --git a/errors/next-script-for-ga.mdx b/errors/next-script-for-ga.mdx
index 55292193558ff..c1d67f8af5e0b 100644
--- a/errors/next-script-for-ga.mdx
+++ b/errors/next-script-for-ga.mdx
@@ -1,8 +1,8 @@
---
-title: Using Google Analytics with Next.js (through `next/script`)
+title: Using Google Analytics with Next.js (through `@next/third-parties/google`)
---
-> Prefer `next/script` component when using the inline script for Google Analytics.
+> Prefer `@next/third-parties/google` when using the inline script for Google Analytics and Tag Manager.
## Why This Error Occurred
@@ -63,11 +63,42 @@ export default function Page() {
}
```
-> **Good to know:**
->
-> - If you are using the Pages Router, please refer to the [`pages/` documentation](/docs/pages/guides/third-party-libraries).
-> - `@next/third-parties` also supports [Google Tag Manager](/docs/app/guides/third-party-libraries#google-tag-manager) and other third parties.
-> - Using `@next/third-parties` is not required. You can also use the `next/script` component directly. Refer to the [`next/script` documentation](/docs/app/guides/scripts) to learn more.
+### Use `@next/third-parties` to add Google Tag Manager
+
+The `GoogleTagManager` component can be used to add [Google Tag Manager](https://developers.google.com/tag-manager/quickstart) to your page.
+
+```tsx filename="app/layout.tsx" switcher
+import { GoogleTagManager } from '@next/third-parties/google'
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+
{children}
+
+ )
+}
+```
+
+To load Google Tag Manager for a single route, include the component in your page file:
+
+```jsx filename="app/page.js"
+import { GoogleTagManager } from '@next/third-parties/google'
+
+export default function Page() {
+ return
+}
+```
+
+## Good to know
+
+- If you are using the Pages Router, please refer to the [`pages/` documentation](/docs/pages/guides/third-party-libraries).
+- `@next/third-parties` also supports [other third parties](/docs/app/guides/third-party-libraries#google-tag-manager).
+- Using `@next/third-parties` is not required. You can also use the `next/script` component directly. Refer to the [`next/script` documentation](/docs/app/guides/scripts) to learn more.
## Useful Links
diff --git a/packages/eslint-plugin-next/src/rules/next-script-for-ga.ts b/packages/eslint-plugin-next/src/rules/next-script-for-ga.ts
index dc4e0cc4d3d28..d06809f1972ce 100644
--- a/packages/eslint-plugin-next/src/rules/next-script-for-ga.ts
+++ b/packages/eslint-plugin-next/src/rules/next-script-for-ga.ts
@@ -1,23 +1,17 @@
import { defineRule } from '../utils/define-rule'
import NodeAttributes from '../utils/node-attributes'
-const SUPPORTED_SRCS = [
- 'www.google-analytics.com/analytics.js',
- 'www.googletagmanager.com/gtag/js',
-]
-const SUPPORTED_HTML_CONTENT_URLS = [
- 'www.google-analytics.com/analytics.js',
- 'www.googletagmanager.com/gtm.js',
-]
+const GOOGLE_ANALYTICS_URL = 'www.google-analytics.com/analytics.js'
+const GOOGLE_TAG_MANAGER_URL = 'www.googletagmanager.com/gtag/js'
+
+const GOOGLE_ANALYTICS_SRC = GOOGLE_ANALYTICS_URL
+const GOOGLE_TAG_MANAGER_SRC = 'www.googletagmanager.com/gtm.js'
+
const description =
- 'Prefer `next/script` component when using the inline script for Google Analytics.'
+ 'Prefer `@next/third-parties/google` when using the inline script for Google Analytics and Tag Manager.'
const url = 'https://nextjs.org/docs/messages/next-script-for-ga'
-const ERROR_MSG = `${description} See: ${url}`
-
-// Check if one of the items in the list is a substring of the passed string
-const containsStr = (str, strList) => {
- return strList.some((s) => str.includes(s))
-}
+const ERROR_MSG_GOOGLE_ANALYTICS = `Prefer \`GoogleAnalytics\` component from \`@next/third-parties/google\` when using the inline script for Google Analytics. See: ${url}`
+const ERROR_MSG_GOOGLE_TAG_MANAGER = `Prefer \`GoogleTagManager\` component from \`@next/third-parties/google\` when using the inline script for Google Tag Manager. See: ${url}`
export default defineRule({
meta: {
@@ -40,37 +34,46 @@ export default defineRule({
}
const attributes = new NodeAttributes(node)
+ const src = attributes.value('src')
// Check if the Alternative async tag is being used to add GA.
// https://developers.google.com/analytics/devguides/collection/analyticsjs#alternative_async_tag
// https://developers.google.com/analytics/devguides/collection/gtagjs
- if (
- typeof attributes.value('src') === 'string' &&
- containsStr(attributes.value('src'), SUPPORTED_SRCS)
+ if (typeof src === 'string' && src.includes(GOOGLE_ANALYTICS_URL)) {
+ return context.report({
+ node,
+ message: ERROR_MSG_GOOGLE_ANALYTICS,
+ })
+ } else if (
+ typeof src === 'string' &&
+ src.includes(GOOGLE_TAG_MANAGER_URL)
) {
return context.report({
node,
- message: ERROR_MSG,
+ message: ERROR_MSG_GOOGLE_TAG_MANAGER,
})
}
+ const dangerouslySetInnerHTML = attributes.value(
+ 'dangerouslySetInnerHTML'
+ )
// Check if inline script is being used to add GA.
// https://developers.google.com/analytics/devguides/collection/analyticsjs#the_google_analytics_tag
// https://developers.google.com/tag-manager/quickstart
- if (
- attributes.value('dangerouslySetInnerHTML') &&
- attributes.value('dangerouslySetInnerHTML').length > 0
- ) {
- const htmlContent =
- attributes.value('dangerouslySetInnerHTML')[0].value.quasis &&
- attributes.value('dangerouslySetInnerHTML')[0].value.quasis[0].value
- .raw
- if (
+ if (dangerouslySetInnerHTML && dangerouslySetInnerHTML.length > 0) {
+ const quasis = dangerouslySetInnerHTML[0].value.quasis
+ const htmlContent = quasis?.[0]?.value?.raw
+ if (htmlContent && htmlContent.includes(GOOGLE_ANALYTICS_SRC)) {
+ context.report({
+ node,
+ message: ERROR_MSG_GOOGLE_ANALYTICS,
+ })
+ } else if (
htmlContent &&
- containsStr(htmlContent, SUPPORTED_HTML_CONTENT_URLS)
+ htmlContent.includes(GOOGLE_TAG_MANAGER_SRC)
) {
context.report({
node,
- message: ERROR_MSG,
+ message: ERROR_MSG_GOOGLE_TAG_MANAGER,
})
}
}
diff --git a/test/unit/eslint-plugin-next/next-script-for-ga.test.ts b/test/unit/eslint-plugin-next/next-script-for-ga.test.ts
index 5a90d17341ecc..cbd288cd0d7c2 100644
--- a/test/unit/eslint-plugin-next/next-script-for-ga.test.ts
+++ b/test/unit/eslint-plugin-next/next-script-for-ga.test.ts
@@ -3,8 +3,9 @@ import { rules } from '@next/eslint-plugin-next'
const NextESLintRule = rules['next-script-for-ga']
-const ERROR_MSG =
- 'Prefer `next/script` component when using the inline script for Google Analytics. See: https://nextjs.org/docs/messages/next-script-for-ga'
+const url = 'https://nextjs.org/docs/messages/next-script-for-ga'
+const ERROR_MSG_GOOGLE_ANALYTICS = `Prefer \`GoogleAnalytics\` component from \`@next/third-parties/google\` when using the inline script for Google Analytics. See: ${url}`
+const ERROR_MSG_GOOGLE_TAG_MANAGER = `Prefer \`GoogleTagManager\` component from \`@next/third-parties/google\` when using the inline script for Google Tag Manager. See: ${url}`
const tests = {
valid: [
@@ -108,36 +109,7 @@ const tests = {
}`,
errors: [
{
- message: ERROR_MSG,
- type: 'JSXOpeningElement',
- },
- ],
- },
- {
- code: `
- export class Blah extends Head {
- render() {
- return (
-
-
Hello title
qqq
- {/* Google Tag Manager - Global base code */}
-
-
- );
- }
- }`,
- errors: [
- {
- message: ERROR_MSG,
+ message: ERROR_MSG_GOOGLE_TAG_MANAGER,
type: 'JSXOpeningElement',
},
],
@@ -166,7 +138,7 @@ const tests = {
}`,
errors: [
{
- message: ERROR_MSG,
+ message: ERROR_MSG_GOOGLE_ANALYTICS,
type: 'JSXOpeningElement',
},
],
@@ -192,7 +164,7 @@ const tests = {
}`,
errors: [
{
- message: ERROR_MSG,
+ message: ERROR_MSG_GOOGLE_ANALYTICS,
type: 'JSXOpeningElement',
},
],
@@ -222,7 +194,7 @@ const tests = {
}`,
errors: [
{
- message: ERROR_MSG,
+ message: ERROR_MSG_GOOGLE_ANALYTICS,
type: 'JSXOpeningElement',
},
],