Подключение счетчика Яндекс.Метрики в Next.JS
Нам нужно вставить код счетчика в шаблон, чтобы он присутствовал на всех страницах сайта.
Код счетчика Яндекс Метрики для NextJS
<Script id="metrika-counter" strategy="afterInteractive">
{`(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(XXXXXXXX, "init", {
defer: true,
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});`
}
</Script>
Вместо XXXXXXXX вставляете свой id счетчика.
Используя Pages Router
В случае с использованием Pages Router, я вставляю счетчик яндекс метрики в _document.tsx
.
src\pages\_document.tsx
import {Html, Head, Main, NextScript} from 'next/document'
import Script from 'next/script';
export default function Document() {
return (
<Html lang="ru">
<Head/>
<body>
<Main/>
<NextScript/>
<Script id="metrika-counter" strategy="afterInteractive">
{`(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(XXXXXXXX, "init", {
defer: true,
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});`
}
</Script>
</body>
</Html>
)
}
У меня один общий макет
для всего сайта, поэтому я создал Custom App и обернул его этим макетом.<Layout />
src\pages\_app.tsx
import '@/styles/utils/normalize.css'
import '@/styles/_variables.scss'
import '@/styles/globals.scss'
import type {AppProps} from 'next/app'
import { Raleway, Roboto } from "next/font/google";
import Layout from "@/components/Layout/Layout";
import {DarkModeProvider} from "@/context/darkModeContext";
import { IconContext } from 'react-icons';
import { useEffect } from 'react';
import { useRouter } from 'next/router';
// @ts-ignore
import NProgress from 'nprogress';
import "nprogress/nprogress.css";
import SEO from "@/components/SEO/SEO";
const raleway = Raleway({
style: ['normal'],
subsets: ['cyrillic', 'latin'],
fallback: ['open-sans', 'system-ui', 'arial']
});
const roboto = Roboto({
weight: ['400', '500', '700'],
style: ['normal', 'italic'],
subsets: ['latin', 'cyrillic'],
fallback: ['open-sans', 'system-ui', 'arial']
});
/_ Progressbar Configurations _/
NProgress.configure({
easing: "ease",
speed: 500,
showSpinner: false,
});
export default function App({Component, pageProps}: AppProps) {
const router = useRouter();
useEffect(() => {
const start = () => NProgress.start();
const end = () => NProgress.done();
router.events.on("routeChangeStart", start);
router.events.on("routeChangeComplete", end);
router.events.on("routeChangeError", end);
return () => {
router.events.off("routeChangeStart", start);
router.events.off("routeChangeComplete", end);
router.events.off("routeChangeError", end);
}
}, [router.events])
return (
<>
<style jsx global>{`
html {
font-family: ${roboto.style.fontFamily};
}
h1, h2, h3, h4, h5, h6 {
font-family: ${raleway.style.fontFamily};
margin-bottom: 0.5em;
}
`}</style>
<DarkModeProvider>
<IconContext.Provider value={{ color: 'var(--icons-color)' }}>
<Layout>
<Component {...pageProps} />
</Layout>
</ IconContext.Provider>
</DarkModeProvider>
</>
)
}
Подробнее про единый Layout и Custom App в документации: https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts
src\components\Layout\Layout.tsx
import { ReactNode, useEffect, useState } from 'react';
import dynamic from 'next/dynamic';
import Header from '@/components/Header/Header';
import Footer from '../Footer/Footer';
import ScrollProgressBar from '@/components/ScrollProgressBar/ScrollProgressBar';
import { YandexMetrika } from '../YandexMetrika/YandexMetrika';
import SEO from '../SEO/SEO';
const Cursor = dynamic(() => import('../Cursor/Cursor'), { ssr: false });
const ScrollTopButton = dynamic(() => import('../../components/ScrollTopButton/ScrollTopButton'), { ssr: false });
type LayoutProps = {
children?: ReactNode
}
export default function Layout({ children }: LayoutProps) {
return (
<>
<SEO />
<YandexMetrika />
<Cursor />
<Header />
<main className="main-content-pt">{children}</main>
<Footer />
<ScrollTopButton />
<ScrollProgressBar />
</>
); }
В макет вставляю компонент
, который будет отправлять данные в метрику при изменении URL-адреса или параметров в нем.<YandexMetrika />
src\components\YandexMetrika\YandexMetrika.jsx
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
export function YandexMetrika() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
const url = `${pathname}?${searchParams}`
ym(XXXXXXXX, 'hit', url);
}, [pathname, searchParams])
return null
}
Используя App Router
В случае с использованием App Router, вставляем счетчик яндекс метрики в файл layout.tsx
перед закрывающим тегом body
.
app\layout.tsx
import { DarkModeProvider } from '@/context/darkModeContext';
import './globals.css';
import { Header } from '@/components/Header/Header';
import { Suspense } from 'react';
import Footer from '@/components/Footer/Footer';
import NextTopLoader from 'nextjs-toploader';
import YandexMetrika from '@/components/YandexMetrika/YandexMetrika';
import Script from 'next/script';
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<DarkModeProvider>
<html lang="ru">
<body>
<Script id="metrika-counter" strategy="afterInteractive">
{`(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(XXXXXXXX, "init", {
defer: true,
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});`
}
</Script>
<YandexMetrika />
<NextTopLoader height={4} />
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
</DarkModeProvider>
);
}
Туда же импортируем компонент, который будет отправлять данные в метрику при изменении URL-адреса или параметров в нем и добавляем в наш layout.
components\YandexMetrika\YandexMetrika.jsx
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
export default function YandexMetrika() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
const url = `${pathname}?${searchParams}`
ym(XXXXXXXX, 'hit', url);
}, [pathname, searchParams])
return null
}