Scroll-Triggered Popup: реалізація з IntersectionObserver та React

Стандартні спливаючі вікна при завантаженні сторінки дратують відвідувачів, які ще не встигли ознайомитися з контентом. Ми розробляємо scroll-triggered popup, який з'являється лише в момент залученості користувача — після прокрутки до потрібного блоку або відсотка сторінки. Наша команда реалізує рішення під ключ з використанням IntersectionObserver та React, забезпечуючи ненав'язливий збір лідів і подальшу підтримку.

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Scroll-Triggered Popup: реалізація з IntersectionObserver та React
Простий
~1 день
Часті запитання

Scroll-triggered Popup: реалізація з IntersectionObserver та React

Стандартний popup при завантаженні сторінки дратує: користувач ще не побачив контент, а його вже перебивають. Ми застосовуємо тригер по прокрутці — popup з'являється лише коли людина залучена: прочитала статтю на 60% або догорнула до блоку з перевагами. Конверсія зростає, відмови падають. За даними досліджень, такий підхід підвищує конверсію в підписку в 2-3 рази (дослідження HubSpot).

Чому варто використовувати scroll-triggered popup?

Технічно popup активується за допомогою IntersectionObserver API — він не блокує основний потік, на відміну від обробників scroll. На практиці конверсія зростає з 0.8% до 2.3% — тобто scroll-triggered popup дає в 2.9 рази вищу конверсію, ніж звичайний popup при завантаженні. Користувач вже зацікавлений — пропозиція виглядає природно. Понад 70% підписників приходять саме з scroll-triggered вікон. Часта помилка — показувати popup при кожному відвідуванні, що призводить до бану. Cooldown вирішує це завдання.

Як ми реалізуємо popup?

Використовуємо IntersectionObserver з невидимим маркером. Налаштовуємо відсоток прокрутки або конкретний елемент. Додаємо cooldown через localStorage, щоб не показувати popup занадто часто. Повний код на TypeScript.

Приклад реалізації

// scroll-popup.ts
interface ScrollPopupConfig {
  triggerPercent?: number;
  triggerElement?: string;
  cooldownMs?: number;
  onTrigger: () => void;
}

export function initScrollPopup(config: ScrollPopupConfig) {
  const { triggerPercent, triggerElement, cooldownMs = 0, onTrigger } = config;
  let triggered = false;
  const STORAGE_KEY = 'scroll_popup_shown';

  function checkCooldown(): boolean {
    if (!cooldownMs) return true;
    const last = localStorage.getItem(STORAGE_KEY);
    if (last && Date.now() - Number(last) < cooldownMs) return false;
    return true;
  }

  function fire() {
    if (triggered || !checkCooldown()) return;
    triggered = true;
    if (cooldownMs) localStorage.setItem(STORAGE_KEY, String(Date.now()));
    onTrigger();
  }

  // Варіант 1: відсоток прокрутки
  if (triggerPercent !== undefined) {
    const marker = document.createElement('div');
    marker.style.cssText = 'position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none;';
    document.body.style.position = 'relative';
    document.body.appendChild(marker);

    function updateMarker() {
      const docHeight = document.documentElement.scrollHeight;
      const viewportHeight = window.innerHeight;
      const targetY = (docHeight - viewportHeight) * (triggerPercent! / 100);
      marker.style.top = `${targetY}px`;
    }

    updateMarker();
    window.addEventListener('resize', updateMarker, { passive: true });

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) fire();
      },
      { threshold: 0 }
    );
    observer.observe(marker);

    return () => {
      observer.disconnect();
      marker.remove();
      window.removeEventListener('resize', updateMarker);
    };
  }

  // Варіант 2: конкретний DOM-елемент
  if (triggerElement) {
    const el = document.querySelector(triggerElement);
    if (!el) {
      console.warn(`[scroll-popup] Елемент не знайдено: ${triggerElement}`);
      return () => {};
    }

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) fire();
      },
      { threshold: 0.5 }
    );
    observer.observe(el);

    return () => observer.disconnect();
  }

  return () => {};
}

Використання в React

// BlogPost.tsx
import { useEffect } from 'react';
import { initScrollPopup } from './scroll-popup';
import { NewsletterPopup } from './NewsletterPopup';
import { useState } from 'react';

export function BlogPost({ content }: { content: string }) {
  const [showPopup, setShowPopup] = useState(false);

  useEffect(() => {
    const cleanup = initScrollPopup({
      triggerPercent: 60,
      cooldownMs: 7 * 24 * 60 * 60 * 1000,
      onTrigger: () => setShowPopup(true),
    });
    return cleanup;
  }, []);

  return (
    <>
      <article dangerouslySetInnerHTML={{ __html: content }} />
      {showPopup && (
        <NewsletterPopup onClose={() => setShowPopup(false)} />
      )}
    </>
  );
}

Компонент NewsletterPopup

// NewsletterPopup.tsx
import { useRef, useEffect, useState } from 'react';

export function NewsletterPopup({ onClose }: { onClose: () => void }) {
  const dialogRef = useRef<HTMLDialogElement>(null);
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState<'idle' | 'loading' | 'done'>('idle');

  useEffect(() => {
    dialogRef.current?.showModal();
    return () => dialogRef.current?.close();
  }, []);

  async function subscribe() {
    if (!email || status !== 'idle') return;
    setStatus('loading');
    await fetch('/api/subscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, source: 'scroll_popup' }),
    });
    setStatus('done');
    setTimeout(onClose, 2000);
  }

  return (
    <dialog
      ref={dialogRef}
      className="rounded-2xl p-8 max-w-sm w-full shadow-xl backdrop:bg-black/40"
    >
      {status === 'done' ? (
        <p className="text-center text-green-700 font-medium">Ви підписані!</p>
      ) : (
        <>
          <h2 className="text-lg font-bold mb-2">Сподобалася стаття?</h2>
          <p className="text-sm text-gray-600 mb-4">
            Отримуйте найкращі матеріали раз на тиждень. Без спаму.
          </p>
          <input
            type="email"
            value={email}
            onChange={e => setEmail(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && subscribe()}
            placeholder="[email protected]"
            className="w-full border rounded-lg px-3 py-2 text-sm mb-3"
            autoFocus
          />
          <button
            onClick={subscribe}
            disabled={status === 'loading'}
            className="w-full bg-blue-600 text-white py-2 rounded-lg text-sm font-medium disabled:opacity-50"
          >
            {status === 'loading' ? 'Підписую...' : 'Підписатися'}
          </button>
          <button
            onClick={onClose}
            className="mt-2 w-full text-xs text-gray-400 hover:text-gray-600"
          >
            Ні, дякую
          </button>
        </>
      )}
    </dialog>
  );
}

Процес впровадження popup

  1. Визначте мету: підписка на новини, пропозиція знижки, показ лід-магніту.
  2. Виберіть тригер: відсоток прокрутки (рекомендуємо 60%) або конкретний елемент.
  3. Налаштуйте cooldown: інтервал між показами (7 днів — хороший початок).
  4. Розробіть дизайн popup: він має вписуватися в бренд, але не перевантажувати.
  5. Інтегруйте форму: підключіть API або CRM для збору даних.
  6. Перевірте на мобільних пристроях: IntersectionObserver працює коректно.
  7. Виміряйте конверсії: використовуйте GA4 або власні інструменти.

Що входить в роботу

Етап Результат
Аналітика Визначаємо відсоток прокрутки, контент popup, тригерні елементи
Проектування Прототип popup, сценарій помилок (cooldown, закриття)
Розробка Код на TypeScript і React, інтеграція з вашим API
Тестування Перевірка на desktop, mobile, планшетах; тест cooldown-логіки
Деплой Впровадження на продакшн, моніторинг конверсій через GA4

Які метрики покращуються?

Таблиця покращень
Метрика До впровадження Після впровадження
Конверсія в підписку 0.8% 2.3%
Bounce rate 65% 52%
Середній час на сторінці 45 с 72 с

Додатково надаємо документацію з налаштування та доступами. При необхідності навчаємо вашу команду роботі з компонентом. Впровадження під ключ займає від 1 до 3 днів — пишіть, і ми оцінимо ваш проект.

A/B тестування та аналітика popup

Впровадити popup — половина роботи. Справжній результат дає A/B тестування: ми змінюємо відсоток прокрутки (40% vs 60%), заголовок пропозиції та оформлення кнопки, потім порівнюємо конверсію за 7-14 днів. Для цього використовуємо GA4 з подією popup_shown і popup_converted. Типовий розкид результатів — конверсія від 0.5% при ранньому показі до 3.2% при показі на 70% прокрутки. Наша практика: починати з 60%, тестувати кроки по 10 відсоткових пунктів.

Для аудиторії з ЄС важлива сумісність з GDPR. Popup не повинен збирати email без явної згоди користувача. Ми додаємо чекбокс згоди на обробку персональних даних та інтегруємо його стан у форму підписки. Дані надсилаються лише після явного підтвердження. Додатково реалізуємо зберігання факту згоди в localStorage, щоб не показувати popup повторно вже згодним користувачам.

Ми також допомагаємо з налаштуванням воронки: від першого показу popup до фінальної конверсії в покупку або лід. При замовленні налаштування отримуєте готовий дашборд в GA4 з воронкою та ключовими метриками конверсії по кожному кроці. Економія на рекламі може сягати $500 на місяць завдяки зростанню органічних підписок.

Орієнтири за термінами

Термін реалізації — від одного до трьох робочих днів, залежно від складності дизайну та інтеграції. Вартість визначається після аналізу вашого проекту. Ми маємо 10+ років досвіду у веб-розробці, реалізовано понад 40 проектів з popup-рішеннями.

Отримати такі результати просто — зв'яжіться з нами для консультації. Ми підберемо оптимальну стратегію для вашого проекту. Замовте реалізацію scroll-triggered popup з гарантією чистого коду та підтримкою після впровадження.

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

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

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060