Реалізація динамічного контенту на лендінгу за UTM-мітками

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація динамічного контенту на лендінгу за UTM-мітками
Середня
~2-3 робочих дні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація динамічного контенту на лендингу за UTM-мітками

Лендинг з динамічним контентом за UTM — це один URL, який показує різний текст, зображення та офери залежно від рекламної кампанії, яка привела користувача. Замість створення десятків окремих посадкових сторінок для кожної кампанії достатньо настроїти заміну контентних блоків на основі параметрів URL.

Вилучення та збереження UTM-параметрів

Параметри потрібно читати при завантаженні сторінки та зберігати — вони пропадають при переходах за внутрішніми посиланнями:

function extractUtmParams(): Record<string, string> {
  const params = new URLSearchParams(location.search);
  const utm: Record<string, string> = {};

  ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach(key => {
    const val = params.get(key);
    if (val) utm[key] = val;
  });

  return utm;
}

function saveAndGetUtm(): Record<string, string> {
  const fromUrl = extractUtmParams();

  if (Object.keys(fromUrl).length > 0) {
    sessionStorage.setItem('utm', JSON.stringify(fromUrl));
    return fromUrl;
  }

  // Повернення — параметрів у URL нема, беремо з сесії
  return JSON.parse(sessionStorage.getItem('utm') ?? '{}');
}

const utm = saveAndGetUtm();

Карта замін контенту

Створюється JSON-конфіг (може лежати у файлі, в CMS або в базі) — правила підстановки контенту:

const contentMap = {
  // За utm_campaign
  campaigns: {
    'google-search-brand': {
      hero_title: 'Офіційний сайт — найкращий вибір для вашого бізнесу',
      hero_subtitle: 'Прямі поставки. Без посередників.',
      cta_text: 'Отримати персональну пропозицію',
      badge: 'Офіційний партнер',
    },
    'facebook-retargeting-cart': {
      hero_title: 'Ви забули щось важливе',
      hero_subtitle: 'Ваша кошик чекає — завершіть замовлення сьогодні.',
      cta_text: 'Повернутися до кошика',
      cta_url: '/cart',
    },
    'email-promo-march': {
      hero_title: 'Спеціальна пропозиція для підписників',
      hero_subtitle: 'Знижка 15% діє до кінця місяця.',
      cta_text: 'Активувати знижку',
      badge: '-15%',
    },
  },

  // За utm_source (фолбек якщо нема збігу за кампанією)
  sources: {
    'yandex': {
      hero_title: 'Знайшли нас у Яндексі? Правильний вибір.',
      cta_text: 'Дізнатися докладніше',
    },
    'vk': {
      hero_title: 'Ласкаво просимо з ВКонтакте',
      cta_text: 'Переглянути каталог',
    },
  },

  // Дефолт
  default: {
    hero_title: 'Рішення для вашого бізнесу',
    hero_subtitle: 'Надійно, швидко, без зайвих затрат.',
    cta_text: 'Почати роботу',
  },
};

function getContent(utm: Record<string, string>) {
  return contentMap.campaigns[utm.utm_campaign]
    ?? contentMap.sources[utm.utm_source]
    ?? contentMap.default;
}

Заміна DOM-елементів

Атрибути data-utm-key вказують, яке поле контенту підставляти в елемент:

<h1 data-utm-key="hero_title">Рішення для вашого бізнесу</h1>
<p data-utm-key="hero_subtitle">Надійно, швидко, без зайвих затрат.</p>
<a href="/start" data-utm-key="cta_text" data-utm-href="cta_url" id="main-cta">
  Почати роботу
</a>
<span class="badge" data-utm-key="badge" data-utm-hide-if-empty="true"></span>
function applyDynamicContent(content: Record<string, string>): void {
  document.querySelectorAll('[data-utm-key]').forEach(el => {
    const key = el.getAttribute('data-utm-key')!;
    const value = content[key];

    if (!value) {
      if (el.getAttribute('data-utm-hide-if-empty') === 'true') {
        (el as HTMLElement).style.display = 'none';
      }
      return;
    }

    el.textContent = value;

    const hrefKey = el.getAttribute('data-utm-href');
    if (hrefKey && content[hrefKey]) {
      (el as HTMLAnchorElement).href = content[hrefKey];
    }
  });
}

// Запуск при завантаженні
const content = getContent(utm);
applyDynamicContent(content);

Заміна відбувається до першого рендеру, якщо скрипт стоїть в <head> з атрибутом defer, або одразу в кінці <body>. При SSR краще передавати UTM на сервер та рендерити потрібний варіант одразу.

SSR-варіант на PHP/Laravel

// У Blade-шаблоні
@php
$utm = [
    'utm_source'   => request('utm_source') ?? session('utm_source'),
    'utm_campaign' => request('utm_campaign') ?? session('utm_campaign'),
];

// Зберігаємо в сесію при першому посещенні
if (request('utm_source')) {
    session(['utm_source' => request('utm_source')]);
    session(['utm_campaign' => request('utm_campaign')]);
}

$content = App\Services\UtmContentService::resolve($utm);
@endphp

<h1>{{ $content['hero_title'] }}</h1>
<p>{{ $content['hero_subtitle'] }}</p>
<a href="{{ $content['cta_url'] ?? '/start' }}">{{ $content['cta_text'] }}</a>

SSR-підхід не вирізує миготіння контенту — користувач одразу отримує персоналізовану версію.

Трекінг ефективності варіантів

// Відправляємо в GA4 який варіант бачить користувач
gtag('event', 'utm_content_variant', {
  utm_campaign: utm.utm_campaign ?? 'none',
  utm_source: utm.utm_source ?? 'direct',
  variant_key: utm.utm_campaign ?? utm.utm_source ?? 'default',
  hero_title: content.hero_title?.substring(0, 50),
});

// На клік по CTA
document.getElementById('main-cta')?.addEventListener('click', () => {
  gtag('event', 'utm_cta_click', {
    cta_text: content.cta_text,
    utm_campaign: utm.utm_campaign,
  });
});

У GA4 будується сегментація за utm_content_variant — видно, які кампанії приводять до кліків по CTA, а які закінчуються відмовою.

Графік

JS-заміна контенту з картою кампаній до 20 варіантів: 4-6 годин. SSR-версія на PHP з сессійним зберіганням UTM: 3-4 години. Трекінг варіантів у GA4 із сегментацією: 2 години. Інтеграція з CMS (редактор варіантів в админке): 1-2 дні.