Розробка Progressive Web App (PWA)
Сайт втрачає до 40% трафіку, якщо користувач іде через повільне завантаження? Або кожен другий відвідувач не повертається після першого візиту? Ми допомагаємо впровадити PWA під ключ — це рішення, яке поєднує швидкість вебу та можливості нативних застосунків. PWA встановлюється на домашній екран, працює офлайн і може надсилати push-сповіщення. За 2–4 дні ви отримуєте застосунок, який завантажується в 2 рази швидше на повільних з'єднаннях. За даними Google, PWA збільшують конверсію на 36% і знижують відмови на 15%. Середня вартість залучення клієнта (CAC) при цьому суттєво знижується, що економить бюджет на рекламу.
Як PWA вирішує проблему офлайн-доступу?
Основний біль: користувач втрачає зв'язок — сайт недоступний. Service Worker кешує ключові ресурси (App Shell) та сторінки. Навіть при обриві з'єднання людина бачить інтерфейс, а не помилку. Ми використовуємо три стратегії кешування залежно від типу даних:
| Стратегія | Застосування | Поведінка |
|---|---|---|
| Cache First | Статика (CSS, JS, зображення) | Віддає з кешу, оновлює фоном |
| Network First | HTML-сторінки | Спочатку мережа, при помилці — кеш |
| Stale While Revalidate | Зображення, API | Миттєво з кешу, потім оновлює |
Web App Manifest — основа встановлюваності
Для появи кнопки «Додати на головний екран» потрібен коректний маніфест. Ми налаштовуємо всі поля: name, short_name, icons у 8 розмірах (включаючи maskable), screenshots для Android, shortcuts для швидких дій. Приклад маніфесту:
{ "name": "ТехноМагазин — купити електроніку", "short_name": "ТехноМагазин", "description": "Смартфони, ноутбуки, аксесуари з доставкою", "start_url": "/?source=pwa", "scope": "/", "display": "standalone", "orientation": "portrait-primary", "theme_color": "#1a73e8", "background_color": "#ffffff", "lang": "uk", "dir": "ltr", "icons": [ { "src": "/icons/icon-72.png", "sizes": "72x72", "type": "image/png" }, { "src": "/icons/icon-96.png", "sizes": "96x96", "type": "image/png" }, { "src": "/icons/icon-128.png", "sizes": "128x128", "type": "image/png" }, { "src": "/icons/icon-144.png", "sizes": "144x144", "type": "image/png" }, { "src": "/icons/icon-152.png", "sizes": "152x152", "type": "image/png" }, { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, { "src": "/icons/icon-384.png", "sizes": "384x384", "type": "image/png" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" } ], "screenshots": [ { "src": "/screenshots/mobile-catalog.webp", "sizes": "390x844", "type": "image/webp", "form_factor": "narrow", "label": "Каталог товарів" } ], "shortcuts": [ { "name": "Кошик", "url": "/cart", "icons": [{ "src": "/icons/cart-96.png", "sizes": "96x96" }] }, { "name": "Обране", "url": "/wishlist", "icons": [{ "src": "/icons/heart-96.png", "sizes": "96x96" }] } ], "share_target": { "action": "/share", "method": "POST", "enctype": "multipart/form-data", "params": { "title": "title", "text": "text", "url": "url" } } } <link rel="manifest" href="/manifest.json"> <meta name="theme-color" content="#1a73e8"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="default"> <meta name="apple-mobile-web-app-title" content="ТехноМагазин"> <link rel="apple-touch-icon" href="/icons/icon-192.png"> Додатково: рекомендуємо прочитати документацію Web App Manifest для глибокого розуміння.
Чому Service Worker — основа PWA?
Service Worker — це проксі між браузером і мережею. Він перехоплює запити та вирішує, чи віддавати кеш, надсилати запит або показувати офлайн-сторінку. Без нього PWA не існує. Ми реалізуємо стратегію «Cache First для статики, Network First для HTML, Stale While Revalidate для медіа». Приклад базового Service Worker:
// sw.js — базова стратегія для PWA const CACHE_VERSION = 'v3'; const APP_SHELL = [ '/', '/manifest.json', '/offline.html', '/css/app.css', '/js/app.js', '/fonts/inter-regular.woff2', '/icons/icon-192.png', ]; // Встановлення: кешуємо App Shell self.addEventListener('install', event => { event.waitUntil( caches.open(`shell-${CACHE_VERSION}`) .then(cache => cache.addAll(APP_SHELL)) .then(() => self.skipWaiting()) ); }); // Активація: видаляємо старі кеші self.addEventListener('activate', event => { event.waitUntil( caches.keys() .then(keys => Promise.all( keys.filter(k => !k.endsWith(CACHE_VERSION)) .map(k => caches.delete(k)) )) .then(() => self.clients.claim()) ); }); // Fetch: різні стратегії для різних ресурсів self.addEventListener('fetch', event => { const { request } = event; const url = new URL(request.url); // App Shell — cache first if (APP_SHELL.includes(url.pathname)) { event.respondWith( caches.match(request).then(r => r || fetch(request)) ); return; } // HTML-сторінки — network first, fallback offline if (request.headers.get('Accept')?.includes('text/html')) { event.respondWith( fetch(request) .then(response => { const clone = response.clone(); caches.open(`pages-${CACHE_VERSION}`) .then(cache => cache.put(request, clone)); return response; }) .catch(() => caches.match(request) .then(cached => cached || caches.match('/offline.html')) ) ); return; } // Зображення — stale while revalidate if (request.destination === 'image') { event.respondWith( caches.open(`images-${CACHE_VERSION}`).then(async cache => { const cached = await cache.match(request); const fetchPromise = fetch(request).then(response => { cache.put(request, response.clone()); return response; }); return cached ?? fetchPromise; }) ); } }); Детальніше про Service Worker читайте в довіднику MDN.
Як додати кнопку встановлення на сайт?
Щоб користувач міг встановити PWA, потрібно обробити подію beforeinstallprompt. Ми реалізуємо React-хук useInstallPrompt:
// useInstallPrompt.ts export function useInstallPrompt() { const [installPrompt, setInstallPrompt] = useState<BeforeInstallPromptEvent | null>(null); const [isInstalled, setIsInstalled] = useState(false); useEffect(() => { const handler = (e: BeforeInstallPromptEvent) => { e.preventDefault(); setInstallPrompt(e); }; window.addEventListener('beforeinstallprompt', handler as EventListener); window.addEventListener('appinstalled', () => setIsInstalled(true)); // Перевірити — чи вже встановлено? if (window.matchMedia('(display-mode: standalone)').matches) { setIsInstalled(true); } return () => window.removeEventListener('beforeinstallprompt', handler as EventListener); }, []); const install = async () => { if (!installPrompt) return; const result = await installPrompt.prompt(); if (result.outcome === 'accepted') { setIsInstalled(true); setInstallPrompt(null); } }; return { canInstall: !!installPrompt && !isInstalled, install, isInstalled }; } // Використання в компоненті function InstallBanner() { const { canInstall, install } = useInstallPrompt(); if (!canInstall) return null; return ( <div className="install-banner"> <p>Встановіть застосунок для швидкого доступу</p> <button onClick={install}>Встановити</button> </div> ); } Приклад offline-сторінки
<!-- /offline.html --> <!DOCTYPE html> <html lang="uk"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Немає з'єднання — ТехноМагазин</title> <style> body { font-family: system-ui; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } .offline { text-align: center; } </style> </head> <body> <div class="offline"> <svg><!-- іконка wifi off --></svg> <h1>Немає з'єднання</h1> <p>Перевірте підключення до інтернету та оновіть сторінку</p> <button onclick="location.reload()">Спробувати знову</button> </div> </body> </html> Чому PWA не встановлюється: типові помилки
Навіть при коректному маніфесті та Service Worker встановлення може не спрацювати. Часта причина — відсутність HTTPS або неправильні іконки. На iOS потрібно явно вказати apple-mobile-web-app-capable і додати іконки потрібних розмірів. Ще одна проблема: браузер відхиляє встановлення, якщо сайт не відповідає критеріям якості (Lighthouse PWA audit). Ми перевіряємо всі ці моменти: тестуємо на Chrome, Safari, Firefox, Android та iOS. Якщо push-сповіщення не працюють, швидше за все, не налаштовано VAPID-ключ або сертифікат. Всі ці нюанси ми відпрацьовуємо на етапі тестування.
Що входить в роботу
- Web App Manifest — повна конфігурація з іконками, сплешами, шорткатами.
- Service Worker — реєстрація, стратегії кешування, оновлення версій.
- Offline-сторінка — адаптивна, з кнопкою перезавантаження.
- Install Prompt — кнопка встановлення на десктоп та мобільні.
- Push-сповіщення — опціонально, інтеграція з Firebase або стороннім сервісом.
- Аудит Lighthouse — гарантуємо зелені перевірки PWA розділу.
- Документація — опис архітектури кешування та інструкція з оновлення.
Строки розробки
| Етап | Час |
|---|---|
| Маніфест і мета-теги | 0.5 дня |
| Service Worker + стратегії | 1–1.5 дня |
| Offline-сторінка | 0.5 дня |
| Install Prompt і тестування | 0.5 дня |
| Push-сповіщення (опціонально) | +1–2 дні |
Підсумковий термін: від 2 до 4 днів без push, до 6 днів з ними. Вартість розраховується індивідуально — пишіть, оцінимо ваш проект.
Як ми працюємо
- Аналітика — вивчаємо структуру сайту, визначаємо App Shell.
- Проектування — обираємо стратегії кешування під ваш контент.
- Реалізація — пишемо Service Worker, налаштовуємо маніфест, інтегруємо хуки.
- Тестування — перевіряємо на Chrome, Safari, Firefox, Android та iOS.
- Деплой — завантажуємо на продакшен, моніторимо через Lighthouse.
Наша команда має 5+ років досвіду в PWA. За цей час реалізували проекти для інтернет-магазинів, медіа та SaaS. Гарантуємо підтримку актуальних стандартів і сумісність з останніми версіями браузерів. Зв'яжіться з нами, щоб отримати консультацію — розповімо, як PWA покращить ваш бізнес. Замовте впровадження PWA вже сьогодні.







