Open EvHaus opened 2 years ago
Thanks for the excellent library. I noticed that in Next 13, the library isn't supported with server components. If you want to use the
GoogleAnalytics
component, you must set that component to"use client"
. Since in most cases I want<GoogleAnalytics />
to be set at the root in (app/layout.tsx
) it means that I need the set the top-level component of my app to be a client component, defeating all the benefits of Next 13's server components.If I try to use
<GoogleAnalytics />
in a server component I get this error:error - (sc_server)/node_modules/next/dist/shared/lib/router-context.js (8:37) @ eval error - TypeError: _react.default.createContext is not a function at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/shared/lib/router-context.js:8:38) at Object.(sc_server)/./node_modules/next/dist/shared/lib/router-context.js (/home/myproject/.next/server/app/page.js:880:1) at __webpack_require__ (/home/myproject/.next/server/webpack-runtime.js:33:43) at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/client/router.js:24:22) at Object.(sc_server)/./node_modules/next/dist/client/router.js (/home/myproject/.next/server/app/page.js:671:1) at __webpack_require__ (/home/myproject/.next/server/webpack-runtime.js:33:43) at eval (webpack-internal:///(sc_server)/./node_modules/next/router.js:2:18) at Object.(sc_server)/./node_modules/next/router.js (/home/myproject/.next/server/app/page.js:1177:1) at __webpack_require__ (/home/myproject/.next/server/webpack-runtime.js:33:43) at eval (webpack-internal:///(sc_server)/./node_modules/nextjs-google-analytics/dist/hooks/usePageViews.js:7:18) { type: 'TypeError', page: '/' }
I suspect it's because of the
useEffect
call inusePageViews
.The
next-auth
team was also recently struggling with a similar issue I believe (see comment).I'm curious of there is any way to make this library work in a server component (ie. not use any
useEffect
calls)?
You may want to create a custom component with “use client”
flag and return <GoogleAnalytics />
to solve this problem until the package become compatible to Next.js 13. It is a official method provided in the beta documentation.
See https://beta.nextjs.org/docs/rendering/server-and-client-components#third-party-packages
Another problem with the /app
folder is the lack of router events in next/navigation
: https://github.com/vercel/next.js/issues/45499. This may get fixed by https://github.com/vercel/next.js/pull/46391.
In addition to the above you'd also need to account for how ga wants to track server side apps:
const gtmDomain = process.env.NEXT_PUBLIC_GTM_DOMAIN;
gtag('config', '${gaMeasurementId}', {
page_path: window.location.pathname,
transport_url: 'https://${gtmDomain}',
first_party_collection: true
});
Thanks for the info. I will try your suggestions.
Based on the info above, I got my answer and it worked for me. Here is my solution:
// AnalyticsProvider component 'use client';
import React from 'react';
import Script from 'next/script';
type Props = { children: React.ReactNode };
export default function AnalyticsProvider(children: Props) { // if you use env file // const gaMeasurementId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID; // const domain = process.env.NEXT_PUBLIC_GTM_DOMAIN; // const reportDomain = process.env.NEXT_PUBLIC_GTM_REPORT_DOMAIN;
// or just hard code const gaMeasurementId = 'your_measurement_id'; const domain = 'https://www.googletagmanager.com'; const reportDomain = 'https://www.google-analytics.com';
return (
<>
{children}
{/ I tried importing the Google Analytics component from the nextjs-google-analytics package, but it didn't work.
So I chose to write the script manually. /}
{/ ${domain}/gtag/js?id=${gaMeasurementId}
}
strategy="afterInteractive"
/>
<Script
id="google-analytics"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaMeasurementId}', {
page_path: window.location.pathname,
transport_url: '${reportDomain}',
first_party_collection: true
});
`,
}}
/>
</>
); }
2. Wrap your html elements with the `AnalyticsProvider` component in `app/layout.tsx`
```tsx
// app/layout.tsx
// import the AnalyticsProvider you just created
import AnalyticsProvider from 'xxxx';
type Props = {
children: React.ReactNode;
};
export default async function RootLayout({ children }: Props) {
return (
<html lang="en">
<body>
<AnalyticsProvider>
{/* your html element */}
{children}
</AnalyticsProvider>
</body>
</html>
);
}
Based on the info above, I got my answer and it worked for me. Here is my solution:
- create a custom component with “use client” flag.
// AnalyticsProvider component 'use client'; import React from 'react'; import Script from 'next/script'; type Props = { children: React.ReactNode }; export default function AnalyticsProvider(children: Props) { // if you use env file // const gaMeasurementId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID; // const domain = process.env.NEXT_PUBLIC_GTM_DOMAIN; // const reportDomain = process.env.NEXT_PUBLIC_GTM_REPORT_DOMAIN; // or just hard code const gaMeasurementId = 'your_measurement_id'; const domain = 'https://www.googletagmanager.com'; const reportDomain = 'https://www.google-analytics.com'; return ( <> {children} {/* I tried importing the Google Analytics component from the nextjs-google-analytics package, but it didn't work. So I chose to write the script manually. */} {/* <GoogleAnalytics trackPageViews /> */} <Script id="google-analytics-js-cdn" src={`${domain}/gtag/js?id=${gaMeasurementId}`} strategy="afterInteractive" /> <Script id="google-analytics" strategy="afterInteractive" dangerouslySetInnerHTML={{ __html: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', '${gaMeasurementId}', { page_path: window.location.pathname, transport_url: '${reportDomain}', first_party_collection: true }); `, }} /> </> ); }
- Wrap your html elements with the
AnalyticsProvider
component inapp/layout.tsx
// app/layout.tsx // import the AnalyticsProvider you just created import AnalyticsProvider from 'xxxx'; type Props = { children: React.ReactNode; }; export default async function RootLayout({ children }: Props) { return ( <html lang="en"> <body> <AnalyticsProvider> {/* your html element */} {children} </AnalyticsProvider> </body> </html> ); }
@aifuxi snippet worked for me
Forgive me for being a bit blonde, but does the above snippet mean it's a bit redundant to use this package for the time being when using server components in Next.js 13
You can create a client component that wraps the package component. This can be used in app/layout
"use client";
import { GoogleAnalytics } from "nextjs-google-analytics";
export default function Analytics({ measurementId }) {
return <GoogleAnalytics gaMeasurementId={measurementId} trackPageViews />;
}
Create a client component in your providers/components dir. Then, use the component in your layout file.
"use client";
import type { NextWebVitalsMetric } from "next/app";
import { event, GoogleAnalytics as GAnalytics } from "nextjs-google-analytics";
// Send NextJS Web Vitals to GA
export function reportWebVitals({
id,
name,
label,
value,
}: NextWebVitalsMetric) {
event(name, {
category: label === "web-vital" ? "Web Vitals" : "Next.js custom metric",
value: Math.round(name === "CLS" ? value * 1000 : value), // values must be integers
label: id, // id unique to current page load
nonInteraction: true, // avoids affecting bounce rate.
});
}
export default function GoogleAnalytics() {
return <GAnalytics trackPageViews />;
}
import { ReactNode } from "react";
import Header from "@/ui/header/header";
import Footer from "@/ui/footer/footer";
import GoogleAnalytics from "@/providers/google-analytics";
interface RootLayoutProps {
children: ReactNode;
}
export default function RootLayout({ children }: RootLayoutProps) {
return (
<html lang="en">
<GoogleAnalytics />
<body>
<Header />
<div>{children}</div>
<Footer />
</body>
</html>
);
}
Thanks for the excellent library. I noticed that in Next 13, the library isn't supported with server components. If you want to use the
GoogleAnalytics
component, you must set that component to"use client"
. Since in most cases I want<GoogleAnalytics />
to be set at the root in (app/layout.tsx
) it means that I need the set the top-level component of my app to be a client component, defeating all the benefits of Next 13's server components.If I try to use
<GoogleAnalytics />
in a server component I get this error:I suspect it's because of the
useEffect
call inusePageViews
.The
next-auth
team was also recently struggling with a similar issue I believe (see comment).I'm curious of there is any way to make this library work in a server component (ie. not use any
useEffect
calls)?