В Next.js App Router как корректно инициировать перезагрузку Google AdSense внутри SPA-layout при изменении динамического параметра маршрута?

Привет! Возникла непонятная проблема с рекламными блоками в проекте на Next.js (App Router). У меня сложная вложенная маршрутизация в стиле Silo: внутри динамического роута [typeSlug] лежит свой layout.tsx с общими элементами (сайдбар, навигация) и рекламными блоками, а разные вкладки подгружаются через children.
  • 9 апреля 2026 г. 17:01
Ответы на вопрос 2
Коротко — проблема в том, что при SPA-навигации layout остаётся, DOM блоков AdSense не перерисовывается, а сам скрипт Google не «видит» смену страницы и не запрашивает новый баннер. Решение — 1) загрузить скрипт adsbygoogle один раз (в корневом layout) и 2) при смене параметра маршрута в клиентском компоненте заново инициализировать слот (либо форсировать его размонт/монт). Ниже — проверенный и безопасный шаблон.

1) Загрузите скрипт один раз (root layout, server или client — важно не дублировать):
- в app/layout.tsx (или другом верхнем layout) добавьте:
```
import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <Script
          id="adsense"
          strategy="afterInteractive"
          src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
          data-ad-client="ca-pub-XXXXXXXXXXXX"
          crossOrigin="anonymous"
        />
      </head>
      <body>{children}</body>
    </html>
  )
}
```
(укажите ваш ca-pub id)

2) Создайте клиентский компонент для слота рекламы, который будет реагировать на изменение пути/параметра. Важные моменты: "use client", usePathname из next/navigation, ключ (key) на <ins> по pathname или параметру и вызов (window.adsbygoogle = window.adsbygoogle || []).push({}) в useEffect.

Пример компонента:
```
'use client'

import { useEffect } from 'react'
import { usePathname } from 'next/navigation'

declare global {
  interface Window { adsbygoogle?: any[] }
}

export default function AdSlot({ adSlot, style = { display: 'block' } }) {
  const pathname = usePathname()

  useEffect(() => {
    // Попробуем инициализировать слот; если скрипт ещё не загрузился — вызов не упадёт
    try {
      (window.adsbygoogle = window.adsbygoogle || []).push({})
    } catch (err) {
      console.warn('Adsense push failed', err)
    }
  }, [pathname]) // срабатывает при смене пути / динамического параметра

  return (
    <ins
      className="adsbygoogle"
      style={style}
      data-ad-client="ca-pub-XXXXXXXXXXXX"
      data-ad-slot={adSlot}
      data-ad-format="auto"
      key={pathname} // форсируем размонт/монт при смене пути
    />
  )
}
```

Почему это работает:
- Script загружен единожды (не дублируйте его в вложенных layout).
- Установка ключа key={pathname} заставляет React размонтировать старый <ins> и смонтировать новый DOM-элемент — это необходимо, чтобы AdSense «увидел» новый контейнер.
- (window.adsbygoogle).push({}) сообщает библиотеке, что нужно обработать новый <ins> и подгрузить объявление.

Дополнительные советы и подводные камни:
- Не загружайте adsbygoogle.js в каждом вложенном layout — будет дублирование и ошибки.
- Убедитесь, что компонент помечен как client ("use client"), иначе usePathname не сработает.
- Иногда полезно ждать, пока скрипт загрузится, прежде чем push вызывать; в большинстве случаев push в useEffect после рендера сработает корректно. Если наблюдаете race condition, можно реализовать ожидание window.adsbygoogle с таймаутом.
- Соблюдайте правила AdSense: обновлять объявления на каждой «виртуальной» навигации можно, но не обновляйте их слишком часто (не выполнять авто-рефреш на таймере и т.п.).

Если хотите, могу прислать интеграцию с конкретной структурой вашего layout (покажите фрагменты), или добавить ожидание загрузки скрипта перед push.
ключевое слово — <code>key</code> . Повесь <code>key={pathname}</code> на компонент с рекламой, React при смене пути убьёт старый DOM и пересоздаст с нуля. <code>push({})</code> на уже заполненный <code>&amp;lt;ins&amp;gt;</code> гугл тупо игнорит, поэтому твой useEffect без пересоздания элемента и не помогал.
Похожие вопросы