Реалізація установки PWA на пристрій (Add to Home Screen)
Add to Home Screen — механізм, що дозволяє користувачу встановити веб-приложення на екран пристрою без магазину приложень. Це не просто ярлик: встановлене PWA запускається без браузерної рядки, отримує окрему іконку, з'являється у списку приложень та, на Android, в установщику. Різниця в утриманні користувачів вимірюється: в середньому CTR у встановлених PWA на 50–70% вищий, ніж у звичайних закладок.
Що потрібно для появи промпта
Браузер показує системний промпт установки тільки при одночасному виконанні кількох умов:
- Сайт працює на HTTPS (localhost — виключення при розробці)
- Зареєстрований Service Worker, який перехоплює мінімум fetch-подію
- Присутній
manifest.jsonз обов'язковими полями:name,short_name,start_url,display, та іконками 192px та 512px - Користувач провів на сайті достатньо часу (Chrome застосовує власну евристику, точні пороги не задокументовані публічно)
На iOS немає жодного автоматичного промпта — тільки ручне додавання через «Поділитися → На екран Домой». Це принципіальне обмеження Safari, яке ніяк не обійти.
Перехват подій beforeinstallprompt
Chrome та Edge генерують подію beforeinstallprompt до того, як показати системний діалог. Потрібно перехопити та показати власний UI:
let deferredPrompt = null;
window.addEventListener('beforeinstallprompt', (e) => {
// Запобігаємо автоматичному показу
e.preventDefault();
deferredPrompt = e;
// Показуємо власну кнопку установки
showInstallBanner();
});
async function triggerInstall() {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
// Логуємо результат
gtag('event', 'pwa_install', {
event_category: 'PWA',
event_label: outcome, // 'accepted' | 'dismissed'
});
deferredPrompt = null;
hideInstallBanner();
}
window.addEventListener('appinstalled', () => {
// Приложення встановлено — скриваємо баннери
hideInstallBanner();
deferredPrompt = null;
});
Важливо: prompt() можна викликати тільки один раз. Після виклику deferredPrompt стає невалідним. Якщо користувач відхилив — наступного события beforeinstallprompt доведеться чекати до кількох тижнів (Chrome запропонує не раніше ніж через 90 днів після відмови).
Визначення контексту запуску
Потрібно розуміти, у якому режимі запущено приложення:
function getDisplayMode() {
if (window.matchMedia('(display-mode: standalone)').matches) {
return 'standalone';
}
if (window.matchMedia('(display-mode: fullscreen)').matches) {
return 'fullscreen';
}
if (window.navigator.standalone === true) {
// iOS Safari
return 'standalone-ios';
}
return 'browser';
}
// Перевіримо при завантаженні
const mode = getDisplayMode();
if (mode === 'browser') {
// Можемо показувати підказку про установку
}
// Слухаємо зміни
window.matchMedia('(display-mode: standalone)').addEventListener('change', (e) => {
if (e.matches) {
console.log('App is now in standalone mode');
}
});
Баннер для iOS
Safari на iOS не підтримує beforeinstallprompt. Приходиться показувати власну інструкцію:
function shouldShowIOSPrompt() {
const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);
const isInStandaloneMode = window.navigator.standalone === true;
const hasSeenPrompt = localStorage.getItem('ios-install-prompt-shown');
return isIOS && !isInStandaloneMode && !hasSeenPrompt;
}
if (shouldShowIOSPrompt()) {
showIOSInstructionBanner();
localStorage.setItem('ios-install-prompt-shown', 'true');
}
Баннер повинен показувати іконку кнопки «Поділитися» (може різнитися між версіями iOS) та текст «Натисніть Share → Додати на екран Домой». Іконку краще брати в форматі SVG, що збігається з реальною системною іконкою.
Манівфест
Мінімальний робочий manifest.json:
{
"name": "Назва приложення",
"short_name": "Приложення",
"description": "Описання для магазину",
"start_url": "/?source=pwa",
"scope": "/",
"display": "standalone",
"orientation": "any",
"background_color": "#ffffff",
"theme_color": "#1a73e8",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"screenshots": [
{
"src": "/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "/screenshots/mobile.png",
"sizes": "390x844",
"type": "image/png",
"form_factor": "narrow"
}
]
}
purpose: "maskable" важен для Android — система може обрізати іконку під різні форми. Іконка повинна мати безпечну зону: контент не ближче ніж на 10% від крайів.
screenshots з'являються в діалозі установки в Chrome 108+ — робить промпт більш переконливим.
Аналітика воронки установки
Без аналітики непонятно, де користувачі відваливаються:
const INSTALL_FUNNEL = {
BANNER_SHOWN: 'install_banner_shown',
BANNER_CLICKED: 'install_banner_clicked',
PROMPT_SHOWN: 'install_prompt_shown',
PROMPT_ACCEPTED: 'install_prompt_accepted',
PROMPT_DISMISSED: 'install_prompt_dismissed',
APP_INSTALLED: 'app_installed',
};
function trackInstallStep(step, params = {}) {
gtag('event', step, {
event_category: 'pwa_install_funnel',
...params,
});
}
Стандартна воронка: показ баннера → клік → показ системного промпта → прийняття. Типовий конверт промпта — 20–40% у мотивованої аудиторії.
Часові рамки реалізації
- Манівфест + базовий Service Worker + перехват події: 1 день
- Кастомний баннер установки з аналітикою: 1 день
- Підтримка iOS (інструкція + визначення режиму): 0.5 дня
- Іконки в форматах maskable + адаптація дизайну: 0.5 дня
Всього: 2–3 дні від нуля до повної реалізації з аналітикою.







