Подключение счетчика Яндекс Метрики в Next.js
Как установить метрику в приложение на Next.js? Нужно код счетчика вставить в шаблон таким образом, чтобы он присутствовал на всех страницах сайта.
Для этого я буду использовать код счётчика и компонент, который будет отслеживать изменения в URL-адресе и фиксировать просмотры.
Решение основано на официальной документации Яндекса.
Код счетчика
Для подключения Яндекс Метрики к Next.js используем компонент <Script />
.
Код счетчика Яндекс Метрики для Next.js
<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 счетчика.
Не забудьте указать defer: true
, это отключит автоматическую отправку данных о просмотрах.
Согласно официальной документации яндекса по установке и настройке счетчика для SPA-сайтов, фиксировать показы страниц необходимо с помощью функции hit
, ее нужно вызывать при каждом изменении страницы.
Используем hit
внутри компонента YandexMetrika()
. Показы будут фиксироваться при изменении URL-адреса или параметров в нем.
Код компонента YandexMetrika
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
}
Вместо XXXXXXXX
вставляем свой id счетчика.
Код счётчика и компонент готовы. Теперь покажу пример подключения для более старого Next.js: Pages Router
и нового App Router
.
Используя Next.js: 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>
<Suspense fallback={<></>}>
<YandexMetrika />
</Suspense>
<NextTopLoader height={4} />
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
</DarkModeProvider>
);
}
Туда же импортируем компонент <YandexMetrika />
, который будет отправлять данные в метрику при изменении URL-адреса или параметров в нем и добавляем в наш Layout. Для Next.js 14 оборачиваем компонент в <Suspense>
, чтобы избежать prerendering Error.
Для чистоты кода, можете вынести <Script />
с счетчиком в отдельный компонент или оставить как в примере.
Используя Next.js: 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>
)
}
Компонент <YandexMetrika />
будет находиться внутри общего макета <Layout />
, в проекте он у меня один, поэтому я создал Custom App (файл _app.tsx) и обернул его этим макетом.
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 />
</>
); }
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";
// ...other code
export default function App({Component, pageProps}: AppProps) {
// ...other code
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 написано в официальной документации: Pages and Layouts
Официальная документация Яндекса по установке и настройке счетчика для SPA-сайтов