diff --git a/next.config.ts b/next.config.ts
index 3833d3d..a11f508 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -10,6 +10,7 @@ const nextConfig: NextConfig = {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_AUTH_REDIRECT_URL: process.env.NEXT_PUBLIC_AUTH_REDIRECT_URL,
+ NEXT_PUBLIC_YM_ID: process.env.NEXT_PUBLIC_YM_ID,
},
images: {
remotePatterns: [
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index f9b67b4..fda543d 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -9,10 +9,11 @@ import { hasLocale, NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
import clsx from "clsx";
-import YandexMetrika from "@/components/analytics/YandexMetrika";
+import { PageViewTracker,YandexMetrika } from "@/components/analytics";
import { loadChatsList } from "@/entities/chats/loaders";
import { loadUser, loadUserId } from "@/entities/user/loaders";
import { routing } from "@/i18n/routing";
+import { AnalyticsProvider } from "@/providers/analytics-provider";
import { AppUiStoreProvider } from "@/providers/app-ui-store-provider";
import { AudioProvider } from "@/providers/audio-provider";
import { ChatsInitializationProvider } from "@/providers/chats-initialization-provider";
@@ -68,24 +69,29 @@ export default async function RootLayout({
return (
+ {/* Analytics Components */}
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+
);
diff --git a/src/components/analytics/PageViewTracker.tsx b/src/components/analytics/PageViewTracker.tsx
new file mode 100644
index 0000000..d8770ef
--- /dev/null
+++ b/src/components/analytics/PageViewTracker.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import { useEffect } from "react";
+import { usePathname, useSearchParams } from "next/navigation";
+
+/**
+ * Wait for Yandex Metrika to be loaded
+ * Retry mechanism with timeout to handle async script loading
+ */
+async function waitForYandexMetrika(maxAttempts = 10, delayMs = 100): Promise {
+ for (let i = 0; i < maxAttempts; i++) {
+ if (typeof window !== "undefined" &&
+ typeof window.ym === "function" &&
+ window.__YM_COUNTER_ID__) {
+ return true;
+ }
+ await new Promise(resolve => setTimeout(resolve, delayMs));
+ }
+ return false;
+}
+
+/**
+ * Page View Tracker Component
+ *
+ * Tracks page views in Yandex Metrika
+ * when route changes occur (client-side navigation).
+ *
+ * Must be included in the app layout or root component.
+ */
+export function PageViewTracker() {
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+
+ useEffect(() => {
+ const url = pathname + (searchParams?.toString() ? `?${searchParams.toString()}` : "");
+
+ // Track page view in Yandex Metrika (with retry logic)
+ const trackYandexMetrika = async () => {
+ const isYmAvailable = await waitForYandexMetrika();
+
+ if (isYmAvailable && typeof window.ym === "function") {
+ const counterId = window.__YM_COUNTER_ID__;
+ if (counterId) {
+ window.ym(counterId, "hit", url);
+ }
+ }
+ };
+
+ // Execute YM tracking
+ trackYandexMetrika();
+ }, [pathname, searchParams]);
+
+ return null;
+}
diff --git a/src/components/analytics/YandexMetrika/YandexMetrika.tsx b/src/components/analytics/YandexMetrika/YandexMetrika.tsx
index 0b4cdcb..0b54592 100644
--- a/src/components/analytics/YandexMetrika/YandexMetrika.tsx
+++ b/src/components/analytics/YandexMetrika/YandexMetrika.tsx
@@ -1,79 +1,52 @@
"use client";
-import { useEffect } from "react";
import Script from "next/script";
-const YANDEX_METRIKA_ID = 103412914;
-
-export default function YandexMetrika() {
- useEffect(() => {
- // Initialize Yandex.Metrika after script loads
- const initializeYandexMetrika = () => {
- if (typeof window.ym === "function") {
- try {
- window.ym(YANDEX_METRIKA_ID, "init", {
- webvisor: true,
- clickmap: true,
- accurateTrackBounce: true,
- trackLinks: true,
- });
- } catch {
- // Silently handle initialization errors
- }
- }
- };
-
- // Check if ym is already available or wait for it
- if (typeof window.ym === "function") {
- initializeYandexMetrika();
- } else {
- // Wait for script to load
- const checkYm = setInterval(() => {
- if (typeof window.ym === "function") {
- initializeYandexMetrika();
- clearInterval(checkYm);
- }
- }, 100);
-
- // Cleanup interval after 10 seconds
- setTimeout(() => {
- clearInterval(checkYm);
- }, 10000);
-
- return () => clearInterval(checkYm);
- }
- }, []);
+import { YANDEX_METRIKA_ID } from "@/shared/constants/analytics";
+/**
+ * Yandex Metrika Integration Component
+ *
+ * Loads Yandex Metrika tracking script dynamically using static counter ID.
+ *
+ * Initializes with: clickmap, trackLinks, accurateTrackBounce, webvisor.
+ * Page views are tracked by PageViewTracker component on route changes.
+ */
+export function YandexMetrika() {
return (
<>
- {/* Yandex.Metrika counter */}
-
- {/* Noscript fallback */}