Инструкция: Подключить Яндекс.Метрику к лендингу

Trigger: агент делает или дорабатывает лендинг и видит что счётчика на странице нет. Либо пользователь говорит «поставь метрику», «настрой аналитику», «как смотреть статистику».

Uses: instructions/create_landing_page, instructions/personal_data_consent, instructions/landing_lead_form

Зачем

Лендинг без счётчика — это слепой запуск: непонятно сколько людей пришло, откуда, что нажимали, сколько заявок принесла каждая рекламная кампания. С Метрикой агент может:

  • Раз в неделю присылать «пришло N, конверсий M, лучший источник — X»
  • Подсказать что блок мёртвый — никто не доскроллил (Вебвизор)
  • Дать данные для ретаргета в Директе

trip2g friendly by default

По умолчанию агент при создании любого лендинга предлагает поставить Метрику и сразу зашивает полный набор целей и параметров — не минимум из «отправил форму», а воронку с 7 уровнями (см. Шаг 4). Идея: один раз настроили → потом видно где именно люди отваливаются.

Если пользователь не хочет аналитику — снять. Дефолт — включено.

Что ставится без вопросов:

Слой Что Зачем
Счётчик tag.js в <head>, асинхронный Базовая загрузка
Опции счётчика clickmap, trackLinks, accurateTrackBounce, webvisor Карта кликов + ссылки + точный отказ + запись поведения
Цели воронки 12 целей по 7 слоям Видеть где отвал
Составная цель «Лид» form_submitted ∪ phone_click ∪ messenger_click Считать реальных лидов из всех каналов
UTM-разметка Чтение utm_* из URL → ym('params', {...}) Сегментация по кампаниям
Параметр визита landing_slug, ab_variant Сравнение лендингов и вариантов
Cookies-плашка defer: true + ручной hit после согласия Совместимость с instructions/personal_data_consent

Когда агент должен это предложить

🤖 Агент сам инициирует разговор о Метрике в трёх случаях:

  1. При сборке нового лендинга (фаза 3 в instructions/create_landing_page) — спросить «ставим Метрику?». Дефолт — да.
  2. На существующем лендинге без счётчика — увидел grep -L 'mc.yandex.ru' <slug>.md → один раз напомнить: «у тебя на лендинге нет аналитики, без неё непонятно работает ли он».
  3. Когда человек жалуется «не понимаю откуда приходят» — это симптом отсутствия Метрики.

Не повторять напоминание, если человек один раз сказал «не надо».

Шаги

Шаг 1 — Создать счётчик (делает человек)

Объяснить пользователю и дать прямую ссылку:

«Создай счётчик: https://metrika.yandex.ru/add
Нужен Яндекс ID. На форме укажи:

  • Название — название лендинга или бренда
  • Адрес сайта — твой домен (тот же что на лендинге)
  • Часовой пояс — твой
  • Принимаю условия — галка

На вкладке «Счётчик» включи:

  • Вебвизор (если хочешь видеть запись действий посетителей)
  • Карта скроллинга
  • Карта кликов

Готово → пришли мне номер счётчика (8 цифр, формат 12345678)».

Шаг 2 — Сохранить номер счётчика

Записать в extra_creds.md (или user_settings.md):

YANDEX_METRIKA_ID=12345678
YANDEX_METRIKA_DOMAIN=example.com   # на каком домене стоит

Шаг 3 — Вставить код в layout

Код вставляется один раз в общий layout лендинга (_layouts/iiminion/index.html или аналог), а не в каждую заметку. Размещение — как можно ближе к началу <head>.

Вариант A: лендинг без cookies-плашки (если на сайте нет instructions/personal_data_consent-плашки EU/RU):

<!-- Yandex.Metrika counter -->
<script>
  (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();
  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({{ METRIKA_ID }}, "init", {
    clickmap: true,
    trackLinks: true,
    accurateTrackBounce: true,
    webvisor: true
  });
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/{{ METRIKA_ID }}" style="position:absolute;left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->

{{ METRIKA_ID }} подставить из сохранённого YANDEX_METRIKA_ID.

Вариант B: лендинг с cookies-плашкой (рекомендуется для РФ и обязательно для EU, см. instructions/personal_data_consent):

Идея: счётчик грузим сразу, но первый хит откладываем до согласия. Параметр defer: true отключает автоматический хит при загрузке страницы — его придётся вызвать руками после клика «Принять».

<script>
  (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();
  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({{ METRIKA_ID }}, "init", {
    defer: true,             // не слать хит автоматически
    clickmap: true,
    trackLinks: true,
    accurateTrackBounce: true,
    webvisor: true
  });

  // Если согласие уже было дано раньше — стрельнуть хит сейчас
  if (localStorage.getItem('cookie-consent') === 'ok') {
    ym({{ METRIKA_ID }}, 'hit', window.location.href);
  }
</script>

В обработчике кнопки «Принять» из плашки cookies (см. instructions/personal_data_consent шаг 5) — добавить запуск хита:

document.getElementById('cookie-ok').onclick = function(){
  localStorage.setItem('cookie-consent', 'ok');
  document.getElementById('cookie-banner').hidden = true;
  if (typeof ym === 'function') {
    ym({{ METRIKA_ID }}, 'hit', window.location.href);
  }
};

Этот же подход — единственный совместимый с GDPR для EU-лендингов. Google Consent Mode v2 у Метрики нет, поэтому только «до согласия не считаем».

Шаг 4 — Зашить полный набор целей (воронка 7 уровней)

Без целей аналитика бесполезна — есть трафик, но непонятно работает ли лендинг. Дефолтный набор trip2g — это воронка: каждая цель показывает следующий уровень вовлечённости. Видно где люди отваливаются и почему.

Слой 1 — Привлечение

Считается автоматически при hit (или при ручном после согласия cookies). Отдельная цель не нужна.

Слой 2 — Внимание (не отвалился сразу)

Цель Тип Имя Что значит
Доскроллил до середины JS-событие scroll_50 Прошёл первый экран
Доскроллил до конца JS-событие scroll_100 Дочитал до футера
Прожил 15 секунд JS-событие + notBounce engaged_15s Не отказ

Сниппет в layout (один раз для всех лендингов):

<script>
(function(){
  if (typeof ym !== 'function') return;
  var id = {{ METRIKA_ID }};
  var fired = {};
  function hit(g){ if (fired[g]) return; fired[g] = true; ym(id, 'reachGoal', g); }

  setTimeout(function(){ ym(id, 'notBounce'); hit('engaged_15s'); }, 15000);

  window.addEventListener('scroll', function(){
    var h = document.documentElement;
    var pct = (h.scrollTop + window.innerHeight) / h.scrollHeight * 100;
    if (pct >= 50) hit('scroll_50');
    if (pct >= 95) hit('scroll_100');
  }, { passive: true });
})();
</script>

Слой 3 — Интерес (просмотр ключевых блоков)

Цель Тип Имя
Увидел блок с ценой JS-событие view_pricing
Увидел блок с оффером JS-событие view_offer
Увидел блок с отзывами JS-событие view_social_proof

Через Intersection Observer (вешается на элементы с data-track-view):

<section data-track-view="view_pricing">...</section>

<script>
(function(){
  if (typeof ym !== 'function' || !window.IntersectionObserver) return;
  var id = {{ METRIKA_ID }};
  var fired = {};
  var io = new IntersectionObserver(function(entries){
    entries.forEach(function(e){
      if (!e.isIntersecting) return;
      var g = e.target.getAttribute('data-track-view');
      if (g && !fired[g]) { fired[g] = true; ym(id, 'reachGoal', g); }
    });
  }, { threshold: 0.5 });
  document.querySelectorAll('[data-track-view]').forEach(function(el){ io.observe(el); });
})();
</script>

Слой 4 — Намерение (взаимодействие с CTA и формой)

Цель Тип Имя Params
Клик по CTA JS-событие cta_click { source: 'hero' | 'pricing' | 'footer' }
Фокус в форме JS-событие form_focus
<a href="#contact"
   onclick="ym({{ METRIKA_ID }}, 'reachGoal', 'cta_click', { source: 'hero' })">
   Оставить заявку
</a>

<script>
document.addEventListener('focusin', function(e){
  if (e.target.closest('form') && !window.__formFocusFired) {
    window.__formFocusFired = true;
    ym({{ METRIKA_ID }}, 'reachGoal', 'form_focus');
  }
});
</script>

Слой 5 — Альтернативный контакт (минуя форму)

Многие лиды уходят в Telegram/WhatsApp/звонок — без этих целей они «невидимы».

Цель Тип Имя
Клик по телефону JS-событие phone_click
Клик по email JS-событие email_click
Клик по Telegram JS-событие telegram_click
Клик по WhatsApp JS-событие whatsapp_click

Универсальный обработчик в layout:

<script>
document.addEventListener('click', function(e){
  var a = e.target.closest('a'); if (!a || typeof ym !== 'function') return;
  var id = {{ METRIKA_ID }};
  var href = a.getAttribute('href') || '';
  if (href.startsWith('tel:'))     ym(id, 'reachGoal', 'phone_click');
  else if (href.startsWith('mailto:')) ym(id, 'reachGoal', 'email_click');
  else if (/t\.me\/|telegram\.me\//.test(href)) ym(id, 'reachGoal', 'telegram_click');
  else if (/wa\.me\/|whatsapp\.com\//.test(href)) ym(id, 'reachGoal', 'whatsapp_click');
});
</script>

Слой 6 — Конверсия

Цель Тип Имя Params
Форма отправлена JS-событие form_submitted { has_phone, has_message, message_length }
Дошёл до thanks Посещение URL thanks_visit URL /thanks

В обработчике submit формы (см. instructions/landing_lead_form шаг 3) после успешного ответа GraphQL:

if (typeof ym === 'function') {
  ym({{ METRIKA_ID }}, 'reachGoal', 'form_submitted', {
    has_phone: !!fields.phone,
    has_message: !!fields.message,
    message_length: (fields.message || '').length
  });
}
window.location.href = spec.success_url || '/thanks';

thanks_visit — резервная цель на случай если JS не сработает. Создаётся в кабинете как «Посещение URL» c условием URL содержит /thanks.

Слой 7 — Составная цель «Лид»

Объединяет все каналы получения контакта: форма + клик по телефону + клик по мессенджеру. Это и есть «настоящее» количество лидов.

В кабинете → «Цели» → «Добавить» → тип «Составная цель» → один шаг с условием:

form_submitted ИЛИ phone_click ИЛИ telegram_click ИЛИ whatsapp_click

Имя: lead. Эту цель потом таскать в Директ для оптимизации кампаний.

Сводка: что создать в кабинете

Прямая ссылка на цели: https://metrika.yandex.ru/list? → твой счётчик → «Цели» → «Добавить цель».

Все JS-цели создаются типом «JavaScript-событие» с именем идентификатора. Названия в кабинете должны совпадать с теми, что в коде.

JS-события:    scroll_50, scroll_100, engaged_15s,
               view_pricing, view_offer, view_social_proof,
               cta_click, form_focus,
               phone_click, email_click, telegram_click, whatsapp_click,
               form_submitted
Посещение URL: thanks_visit  (URL содержит /thanks)
Составная:     lead          (form_submitted ИЛИ *_click)

Шаг 4.1 — UTM и параметры визита (для сегментации)

Без этого все источники сваливаются в одну кучу. С этим — в отчётах видно «из Telegram-канала пришло 40, конвертили 5; из Директа 120, конвертили 3».

В layout, после инициализации Метрики:

<script>
(function(){
  if (typeof ym !== 'function') return;
  var p = new URLSearchParams(location.search);
  var params = {
    landing_slug: '{{ SLUG }}'               // имя лендинга
  };
  ['utm_source','utm_medium','utm_campaign','utm_term','utm_content'].forEach(function(k){
    var v = p.get(k); if (v) params[k] = v;
  });
  // A/B-вариант если используется
  var ab = document.documentElement.getAttribute('data-ab');
  if (ab) params.ab_variant = ab;

  ym({{ METRIKA_ID }}, 'params', params);
})();
</script>

{{ SLUG }} подставить slug лендинга — будет видно если на счётчике несколько лендингов.

После этого в отчёте «Параметры визитов» можно строить срезы: «конверсии по utm_source», «отказы по landing_slug», «CR по ab_variant».

Шаг 5 — Проверить что счётчик и цели работают

  1. Открыть лендинг в incognito-вкладке с параметром ?utm_source=test
  2. Если есть cookies-плашка — нажать «Принять»
  3. Прокрутить страницу до конца, подождать 15 секунд
  4. Кликнуть по любой CTA-кнопке (но вернуться назад)
  5. Открыть форму, поставить курсор в первое поле, заполнить и отправить
  6. В кабинете Метрики → отчёт «По времени» → визит появляется за ~30 секунд
  7. Отчёт «Конверсии» → должны быть: engaged_15s, scroll_50, scroll_100, cta_click, form_focus, form_submitted, lead
  8. Отчёт «Параметры визитов» → видны utm_source: test, landing_slug: <slug>

Если визит не пришёл за 2 минуты:

  • grep -E 'METRIKA_ID|XXXXXX' <layout> — убедись что подстановка сработала
  • Консоль браузера → нет ли CSP-ошибок (script-src должен пускать mc.yandex.ru)
  • Если defer: trueym(..., 'hit', ...) точно вызывается после согласия
  • Если цели не сработали — имена в кабинете посимвольно совпадают с кодом

Шаг 6 — Поставить себе крон на еженедельный отчёт (опционально)

/cron create "Раз в неделю принеси сводку по Метрике лендинга <slug>:
сколько визитов, конверсий form_submitted, лучший источник трафика, отказы.
Используй Metrika API token из extra_creds.md (если есть) или попроси
человека прислать скрин из кабинета."
--schedule "0 10 * * mon"

API Метрики требует отдельный OAuth-токен — если пользователь даст, агент сможет тянуть отчёты сам. Без токена — крон просто напомнит человеку открыть кабинет.

Проверка результата

  • На лендинге в head есть код Метрики, {{ METRIKA_ID }} подставлен
  • В кабинете Метрики появляются визиты (incognito-тест)
  • Цель form_submitted срабатывает при отправке формы
  • Если есть плашка cookies — счётчик не шлёт хит до согласия

Когда остановиться

  • Счётчик создан, ID сохранён в extra_creds.md
  • Код в layout, хит идёт (с учётом плашки если она есть)
  • Все 13 JS-целей + thanks_visit + составная lead созданы в кабинете
  • Имена в коде и в кабинете совпадают посимвольно
  • Параметры визита (landing_slug, utm_*) приходят в отчёт
  • Человек видит данные в кабинете

Типичные ошибки

  • {{ METRIKA_ID }} остался в HTML — счётчик не работает. Проверить grep -E 'METRIKA_ID|XXXXXX' <layout> перед sync.
  • Код вставлен в <body>, а не в <head> — работает, но первые секунды визитов могут потеряться. Переставить.
  • defer: true без ручного hit — счётчик загружен, но визиты не считаются. Добавить ym(..., 'hit', ...) после согласия.
  • Цель в коде есть, в кабинете не созданаreachGoal уходит впустую, в отчётах ноль. Создать цель с тем же именем.
  • Webvisor включён, а в политике конфиденциальности про запись действий ни слова — нарушение. Дописать строку в legal/personal_data_consent про запись поведения.
  • Один счётчик на несколько разных лендингов — отчёты смешиваются. Лучше один счётчик на один продукт; разделять источники через UTM, а не через счётчики.
  • Поставили счётчик, не настроили ни одной цели — есть трафик, нет конверсий, лендинг непонятно работает или нет. Минимум form_submitted обязателен.

Кейсы

Кейс 1: новый лендинг, метрики у пользователя ещё не было
→ Шаг 1 (создать) → Шаг 2 (сохранить ID) → Шаг 3 вариант A или B → Шаг 4 (цель form_submitted) → Шаг 5 (проверить).

Кейс 2: лендинг уже сделан, счётчика нет
→ Напомнить про метрику → если согласен, дальше как кейс 1 со Шага 1.

Кейс 3: метрика стоит, но целей нет
→ Сразу Шаг 4 (зашить полный набор) → Шаг 5 (проверить).

Кейс 4: EU-аудитория
→ Обязательно вариант B (Шаг 3) + плашка cookies из instructions/personal_data_consent с равноценными кнопками «Принять» / «Отклонить». На «Отклонить» — хит не запускать никогда.

Кейс 5: пользователь говорит «слишком много целей, оставь минимум»
→ Сократить до: form_submitted, cta_click, phone_click, telegram_click, составная lead. Это нижняя граница чтобы увидеть конверсии и каналы. Меньше — слепо.

Связано: instructions/create_landing_page, instructions/landing_lead_form, instructions/personal_data_consent, insights/how_to_check_yourself

Источник: официальная документация Яндекс.Метрики — https://yandex.ru/support/metrica/quick-start.html, https://yandex.ru/support/metrica/code/counter-initialize.html, https://yandex.ru/support/metrica/general/goals.html