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 */} -