Розробка 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 вже сьогодні.







