PWA
Прогресивний вебзастосунок, або PWA, — це сайт із service worker і вебманіфестом, який можна встановити, який працює офлайн і поводиться як застосунок. SDK для PWA не існує: що може встановлений вебзастосунок, залежить від браузера й платформи, і саме розрив між Chrome на Android і Safari на iPhone визначає більшість продуктових рішень. Сторінка починається з шістнадцяти можливостей і того, де кожна працює в жовтні 2026 року; далі — service worker і оновлення, яке не приходить, стратегії кешування й сховище, яке можуть очистити, встановлення на чотирьох платформах, що дозволяє Apple і як це змінювалося, питання магазинів застосунків, п’ять задокументованих кейсів і те, де все це вироджується.
- Що це
- Service worker, маніфест і HTTPS
- Найбільший розрив
- iPhone: push лише після встановлення; немає фонової синхронізації, Bluetooth і підказки встановлення
- Дистрибуція
- Посилання; виправлення виходить із наступним деплоєм
Куди дістає вебзастосунок у 2026 році — шістнадцять можливостей, платформа за платформою
PWA — це не одна технологія з одним рівнем підтримки. Це набір можливостей браузера, і кожен браузер реалізує свою підмножину. Тож питання, яке насправді має команда, — «чи можна зробити це вебзастосунком?» — не має однієї відповіді. Відповідь є для кожної можливості й кожної платформи. На iPhone відповідь ще й змінюється, щойно користувач додає сайт на домашній екран, бо частина можливостей існує лише для встановлених вебзастосунків. Кожен браузер на iOS використовує рушій WebKitрушій браузера SafariРушій браузера з відкритим кодом, на якому працює Safari. Сторінка, що працює в Chrome, може зламатися у WebKit, тому набори тестів проганяють браузерні маршрути й у ньому — Playwright для цього має власну збірку WebKit.Усі терміни від Apple, тож користувач iPhone не обійде цього, встановивши Chrome. Ось шістнадцять речей, які зазвичай потрібні застосунку, де кожна працює станом на жовтень 2026 року і що робити там, де ні. Наступні розділи посилаються на рядки за номерами.
| # | Що потрібно застосунку | Android · Chrome | iPhone · Safari, на домашньому екрані | Десктоп | Якщо цього немає |
|---|---|---|---|---|---|
| 1 | Працювати офлайн (service worker і Cache API) | Так | Так, з iOS 11.3 (2018) | Усі сучасні браузери | Це база — спроєктуйте офлайн-стан (розділ 3) |
| 2 | Тримати дані на пристрої (IndexedDB, файли в OPFS) | Так, до 60% диска | Так — але у вкладці Safari дані видаляють після семи днів користування без візиту; встановлені застосунки — виняток | Так; Firefox дає сайту щонайбільше 10 ГіБ, якщо сховище не зроблено постійним | Тримати джерело правди на сервері |
| 3 | Відкриватися у власному вікні, без інтерфейсу браузера | Так, після встановлення | Так; з iOS 26 кожен сайт, доданий на домашній екран, відкривається як вебзастосунок | Chrome і Edge встановлюють; Safari 17 додає в Dock; Firefox 143 закріплює на панелі задач Windows | Закладка теж працює |
| 4 | Камера, мікрофон, геолокація | Так, після запиту дозволу | Так, після запиту дозволу | Так | — |
| 5 | Приймати платежі | Payment Request, Google Pay | Apple Pay і Payment Request | Так | Звичайна форма оплати |
| 6 | Системне меню «Поділитися» (navigator.share) | Так | Так | Chrome, Edge і Safari; Firefox — лише на Android | Кнопка «Копіювати посилання» |
| 7 | Не давати екрану згаснути (Wake Lock) | Так | Так, з iOS 18.4 | Так; Firefox — з версії 126 | — |
| 8 | Push-сповіщення | Так, у браузері чи після встановлення | Лише після додавання на домашній екран (iOS 16.4, березень 2023), і запит має йти після натискання | Так; Safari на Mac — з версії 16 | Email чи SMS для всього критичного |
| 9 | Лічильник на іконці (setAppBadge) | Ні — Android натомість показує крапки сповіщень | Так, після встановлення (iOS 16.4) | Chrome і Edge після встановлення; Safari 17 | Лічильник усередині застосунку |
| 10 | Запропонувати встановлення зі сторінки (beforeinstallprompt) | Так | Ні — користувач мусить сам знайти «Поділитися» → «На домашній екран» | Chrome і Edge; не Safari й не Firefox | Екран з інструкцією для кожної платформи |
| 11 | Сховище, яке ніколи не очищають (navigator.storage.persist()) | Надається мовчки, залежно від того, наскільки користувач активний | Надається мовчки, наприклад встановленим застосункам | Firefox питає користувача | Синхронізація із сервером (рядок 2) |
| 12 | Надіслати відкладені записи, коли повернеться зв’язок, навіть коли застосунок закрито (Background Sync) | Так | Ні | Лише Chrome і Edge | Ставити записи в чергу в IndexedDB і відтворювати, коли застосунок відкриють наступного разу |
| 13 | Оновлювати вміст у фоні за розкладом (Periodic Background Sync) | Лише встановлені застосунки; інтервал обирає браузер | Ні | Лише Chrome і Edge | Оновлювати під час відкриття; тихий push не замінить, бо кожен push мусить показати сповіщення |
| 14 | Великі завантаження, що переживають закриття застосунку (Background Fetch) | Так | Ні | Лише Chrome і Edge | Завантажувати, поки застосунок відкритий, частинами з докачуванням |
| 15 | Пристрої Bluetooth, USB, NFC і послідовного порту | Bluetooth, USB, NFC, а з Chrome 148 — і послідовний порт | Жодного | Bluetooth, USB, послідовний порт і HID у Chrome та Edge; послідовний порт у Firefox 151 — через доповнення | Нативний або супутній застосунок |
| 16 | Отримувати поширене й відкривати файли із системи (Share Target, File Handling) | Share Target — так | Ні | Share Target і File Handling у Chrome та Edge | Кнопка завантаження файлу |
Service worker: проксі, якого сторінка не бачить і не обійде
Усе, що PWA вміє, а звичайний сайт — ні, починається з одного механізму. Service worker — це скрипт, який браузер виконує окремо від сторінки й без доступу до неї. Після реєстрації він отримує подію fetch для кожного запиту сторінок у своїй області — HTML, скриптів, зображень, викликів APIApplication Programming Interface — контракт між програмамиПравила, за якими одна програма звертається до іншої: які запити можна надіслати, у якому вигляді й що прийде у відповідь. Для того, хто звертається, це контракт — усе, що за ним, лишається чорною скринькою, і саме тому другий бік можна переписати, не зламавши вас.Докладніше в статті →Усі терміни. Для кожного він вирішує, що відповісти: щось із кешу, щось із мережі або щось, що збудує сам. Сторінка не може відрізнити одне від іншого. Область — це тека, у якій лежить файл воркера, тож воркер за адресою /sw.js керує всім сайтом, а за адресою /app/sw.js — лише /app/. Service worker працює лише через HTTPS, за винятком localhost під час розробки.
У воркера є життєвий цикл, і більшість проблем у продакшені походить саме з нього. Браузер перевіряє, чи є нова версія, під час кожної навігації на сторінку в області, а також під час подій на кшталт push, якщо не перевіряв протягом останніх 24 годин. Починаючи з Chrome 68, а також в інших сучасних браузерах, ця перевірка ігнорує HTTPHyperText Transfer Protocol — протокол вебуПротокол, яким спілкуються браузер і сервер: клієнт надсилає метод і адресу, сервер відповідає кодом стану й тілом. Між запитами він нічого не памʼятає — кожен запит стоїть сам по собі, тому все, що треба памʼятати, передається явно.Усі терміни-заголовки кешування для самого файлу воркера, хоча не для скриптів, які він імпортує. Якщо відрізняється бодай один байт, браузер завантажує й встановлює новий воркер. А тоді новий воркер чекає. Типово він бере керування лише тоді, коли жодною вкладкою вже не керує старий. До того старий воркер далі відповідає — зі старих кешів.
Продумайте шлях назад ще до першого воркера. Якщо до користувачів потрапив зламаний воркер, виправлення — задеплоїти воркер, який нічого не робить, — без обробника fetch, із skipWaiting() під час встановлення, — рівно за тією самою адресою. Тоді запити проходять так, ніби воркера немає. Перейменуйте файл — і зламаний воркер лишиться зареєстрованим поруч із новим і далі відповідатиме. Заголовок відповіді Clear-Site-Data: "storage" теж знімає реєстрацію воркерів, але він стирає все сховище сайту й підтримується не скрізь, тож це запасний варіант, а не план. І нарешті, пишіть воркер бібліотекою, а не руками. Workbox (7.4), Serwist, vite-plugin-pwa і service worker від Angular генерують список файлів для передкешування з результату збірки, тож список і хеші версій лишаються правильними, поки змінюється бандл.
Кеш і сховище: наскільки застарілою може бути відповідь і скільки вона проживе
Стратегію кешування обирають для кожного виду запитів, а не раз на весь сайт, і вибір зводиться до одного питання: наскільки застарілою може бути ця відповідь? Бандл JavaScript, у назві файлу якого є хеш його вмісту, ніколи не застаріває — нова версія отримує нову назву, — тож він може братися з кешу завжди. Баланс рахунку не має застарівати ніколи, тож він мусить приходити з мережі або не приходити зовсім. Більшість запитів — десь посередині. Три стратегії покривають більшість із них, і відрізняються вони тим, хто відповідає першим.
| Запит | Стратегія | Що користувач бачить офлайн | Пастка |
|---|---|---|---|
| Скрипти, стилі, шрифти з хешем у назві | Передкешування під час встановлення; спершу кеш | Оболонку застосунку, миттєво | index.html без хешу в режимі «спершу кеш» закріплює кожного користувача на одному релізі |
| Навігація між сторінками (HTML) | Спершу мережа з таймаутом у кілька секунд, потім сторінка з кешу або офлайн-сторінка | Останню версію сторінки або сторінку, що каже про офлайн | Без таймауту: на слабкому сигналі користувач дивиться на порожній екран замість сторінки з кешу |
| Читання з API | Спершу мережа; stale-while-revalidate там, де вчорашні дані — нормально | Останні дані з позначкою, коли їх отримано | Старі дані, показані як актуальні, без часу отримання |
| Записи в API | Лише мережа; офлайн — черга в IndexedDB і повторне надсилання | Зміну з позначкою «очікує» | Повтор, що спрацював двічі й двічі списав гроші, — надсилайте з кожним записом ключ ідемпотентності |
| Зображення й шрифти з іншого джерела без CORS | Stale-while-revalidate з лімітом розміру — або зовсім ні | Копію з кешу | Непрозора відповідь ховає свій статус, тож помилку можна закешувати назавжди, а Chrome рахує кожну як щонайменше 7 МБ квоти |
| Вхід, платежі, персональні дані | Лише мережа | Зрозумілу помилку | Персональні відповіді, що лишилися в кеші на спільному пристрої |
Записи потребують найбільшої уваги, бо офлайн-запис — це обіцянка. Звичний патерн — зберегти зміну локально, показати її як таку, що очікує, і надіслати, коли повернеться зв’язок. У Chrome і Edge Background Sync може надіслати її навіть після закриття застосунку (рядок 12). Скрізь інде повторне надсилання стається наступного разу, коли застосунок відкриють. Так чи інакше сервер може отримати той самий запис двічі — раз від спроби, що відвалилася за таймаутом, і раз від повтору, — тож кожен запис має нести ключ, який дозволяє серверу вважати повтор тим самим запитом. Операцію, що дає той самий результат, скільки б разів її не виконали, називають ідемпотентною (idempotentбезпечно повторитиОперація, що дає однаковий результат, скільки б разів її не виконали — раз чи пʼять. Саме це робить повтор безпечним, а оскільки будь-який мережевий виклик може бути повторений без вашого відома, альтернатива — списати з картки двічі.Усі терміни), і офлайн-записи безпечні лише тоді, коли вони такі.
Що браузер має право видалити
| Браузер | Скільки може зберігати один сайт | Коли його видаляють | `persist()` |
|---|---|---|---|
| Chrome, Edge | До 60% диска | Спершу сайти, якими користувалися найдавніше, коли закінчується місце на диску | Надається чи відхиляється мовчки, залежно від активності |
| Firefox | 10% диска або 10 ГіБ на групу сайтів; 50% для постійного сховища | Спершу найдавніше використані, якщо сховище не постійне | Питає користувача |
| Safari, у вкладці | Близько 60% диска | Усе, що записали скрипти, після семи днів користування Safari без візиту; а також за браку місця | Надається чи відхиляється мовчки |
| Safari, застосунок на домашньому екрані | Близько 60% диска | Не за правилом семи днів; але за браку місця — так | Імовірніше буде надано |
Офлайн — це стан, який має бути в інтерфейсі, а не технічна деталь. Показуйте, що застосунок офлайн. Позначайте закешовані дані часом, коли їх отримано. Показуйте список змін, що чекають на надсилання, і кажіть, що сталося, коли якась не пройшла. Заздалегідь вирішіть, що буде, коли той самий запис змінили на двох пристроях, поки обидва були офлайн: перемагає останній запис, сервер об’єднує чи вибирає користувач. Застосунок, що мовчки показує тижневі дані як актуальні, гірший за той, що зізнається, що він офлайн.
Встановлення: що означає «встановити» на чотирьох платформах
Друга половина PWA — вебманіфест: невеликий JSONJavaScript Object Notation — текстовий формат данихТекстовий формат для передавання даних: пари «ключ — значення», списки, числа й рядки. Він народився в JavaScript, але сьогодні його читає будь-яка мова — і саме тому ним відповідає більшість API, а не чимось потужнішим.Усі терміни-файл, на який посилається кожна сторінка і який дає застосунку назву, іконки, стартову адресу й режим показу. Поставте display у standalone — і встановлений застосунок відкриється без адресного рядка й вкладок, а це й є більша частина того, що робить його схожим на застосунок, а не на закладку. Chrome пропонує встановлення, коли сайт працює через HTTPS, а в маніфесті є name чи short_name, іконки на 192 і 512 пікселів, start_url і значення display, — після того як користувач клацнув на сторінці й провів на ній близько 30 секунд. Починаючи з Chrome 108 на Android і 112 на десктопі, для встановлення з меню браузера service worker узагалі не потрібен; підказка встановлення його все ще шукає.
«Встановити» дає чотири різні результати. На Android Chrome створює WebAPK — справжній пакет Android, згенерований з маніфесту, — тож застосунок з’являється серед застосунків і в системних налаштуваннях і відкриває посилання зі своєї області замість браузера. На iPhone користувач додає сайт із меню «Поділитися». З iOS 26, з вересня 2025 року, кожен доданий так сайт типово відкривається як вебзастосунок — із маніфестом чи без. Вебзастосунок на iPhone має власні і сховище, окремі від Safari: користувач, що увійшов на сайті, відкриває нову іконку й бачить, що не увійшов, тож перший екран після встановлення має бути входом, який не схожий на баґ. Chrome і Edge на десктопі встановлюють сайт у власне вікно. Safari на Mac, з версії 17, додає його в Dock. Firefox 143 у вересні 2025 року почав закріплювати сайти на панелі задач Windows.
Лише браузери на Chromium дають самій сторінці запропонувати встановлення (рядок 10). Вони генерують подію beforeinstallprompt, яку сторінка може притримати й відтворити зі своєї кнопки «Встановити» в слушний момент. Скрізь інде встановлення — це пункт меню, який користувач мусить знайти сам, тож продукт, якому потрібні встановлені користувачі, пише короткі інструкції для кожної платформи й показує їх тоді, коли користувач найімовірніше погодиться, — після другого візиту, а не на першому екрані. Стандартний спосіб для сторінки попросити вже на підході: 21 вересня 2026 року Chrome оголосив намір випустити Web Install API — navigator.install() і елемент <install> — у Chrome 156, спершу на десктопі. WebKit заявив, що проти, бо встановлення має починатися в інтерфейсі самого браузера, тож не плануйте під нього фічу для iPhone.
| Поле маніфесту | Що робить | Хто його читає |
|---|---|---|
| name, short_name | Назва застосунку й коротша — для підпису іконки | Кожен браузер, що встановлює |
| icons | Іконки кількох розмірів; "purpose": "maskable" для іконок, які система може обрізати під власну форму | Chromium; Safari — лише коли немає apple-touch-icon |
| start_url, scope | Де відкривається застосунок і які адреси вважаються його частиною | Кожен браузер, що встановлює |
| display | standalone — вікно застосунку; minimal-ui лишає кілька елементів браузера | Кожен браузер, що встановлює |
| id | Стала ідентичність, щоб пізніша зміна start_url не створила другий застосунок | Chromium; Safari — з 16.4 на iOS |
| shortcuts | Дії після довгого натискання на іконку | Chromium; Safari 17.4 на Mac |
| share_target | З’являтися в системному меню «Поділитися» як отримувач (рядок 16) | Chromium |
| file_handlers, protocol_handlers | Відкривати в застосунку файли певного типу чи посилання певної схеми (рядок 16) | Chromium на десктопі |
iPhone: що дозволяє Apple і як до цього дійшло
Рядки 8–11 і порожні клітинки iPhone у рядках 12–16 мають одну причину. На iOS кожен браузер — Safari, Chrome, Firefox, Edge — мусив використовувати рушій WebKit від Apple, тож те, що реалізує WebKit, — це те, що може веб на iPhone. Правило почало слабшати: з iOS 17.4 браузери в ЄС можуть використовувати власні рушії, а з iOS 26.2, з грудня 2025 року, — і браузери в Японії. Але, за власною заявою Apple 2024 року, вебзастосунки на домашньому екрані збудовано безпосередньо на WebKit, і на момент написання жоден великий браузер не постачає на iPhone власного рушія. Плануючи, виходьте з WebKit. Історія пояснює, чому команди раз у раз на цьому спотикаються:
| Коли | iOS | Що змінилося для вебзастосунків |
|---|---|---|
| Червень 2007 | iPhone OS 1 | Apple представила вебзастосунки в Safari як спосіб створювати програми для iPhone; нативний SDK з’явився в березні 2008 |
| Березень 2018 | 11.3 | Service worker і маніфест — уперше офлайн-вебзастосунки на iPhone (рядок 1) |
| Березень 2020 | 13.4 | Safari видаляє все сховище, записане скриптами, після семи днів користування без візиту; застосунки на домашньому екрані — виняток (рядок 2) |
| Березень 2023 | 16.4 | Web Push і лічильники для застосунків на домашньому екрані; інші браузери можуть додавати сайти на домашній екран (рядки 8–9) |
| Лют.–бер. 2024 | 17.4 | Бета-версії перетворили застосунки на домашньому екрані в ЄС на ярлики Safari; рішення скасували 1 березня |
| Березень 2025 | 18.4 | Declarative Web Push, що показує сповіщення без виконання JavaScript; Wake Lock у застосунках на домашньому екрані (рядок 7) |
| Вересень 2025 | 26 | Кожен сайт, доданий на домашній екран, типово відкривається як вебзастосунок; маніфест не обов’язковий (рядок 3) |
| Грудень 2025 | 26.2 | Інші рушії браузерів дозволено в Японії, як у ЄС з 17.4 |
Push на iPhone має три правила, крім «лише для встановлених» (рядок 8). Запит дозволу може з’явитися лише у відповідь на пряму дію, як-от натискання кнопки «Увімкнути сповіщення», — і ніколи під час завантаження сторінки. Кожен push мусить показати сповіщення, яке бачить користувач: WebKit відкликає підписку сайту, чий воркер отримав push і нічого не показав, тож тихого push для фонових оновлень у вебі немає (рядок 13). А з iOS 18.4 декларативний push дає серверу самому описати сповіщення, тож воно з’являється, навіть якщо воркер так і не запуститься. Лічильники працюють за тим самим принципом (рядок 9): встановлений застосунок може поставити число на своїй іконці, і система показує його разом з іншими сповіщеннями застосунку.
PWA, обгортка чи застосунок: питання магазинів
Перевага PWA, що переживає будь-яке порівняння, — дистрибуція. Немає черги рев’ю, акаунта в магазині, сертифіката, який треба поновлювати, і збірки, яку треба підписувати. Виправлення виходить із наступним деплоєм, а посилання — це весь процес встановлення. Для продукту, який знаходять пошуком або отримують посиланням, це різниця між «спробувати зараз» і «спершу щось встановити». Чого посилання не дає — це картки в магазині, де люди гортають застосунки, платіжної системи магазину й довіри, яку частина користувачів має лише до застосунків із магазину. Є чотири способи повернути частину цього, і в кожного своя пастка:
| Шлях | Що постачається | Оновлення | Пастка |
|---|---|---|---|
| Лише PWA | Сайт | Кожен деплой | Немає картки в магазині; на iPhone користувачів треба навчити встановлювати (рядок 10) |
| Google Play як Trusted Web Activity | Тонкий Android-застосунок, що відкриває ваш сайт у Chrome на весь екран (збирає Bubblewrap) | Вебчастина — з кожним деплоєм; оболонка — рідко | Digital Asset Links мають довести, що сайт ваш, інакше з’являється адресний рядок браузера; діють правила Play, зокрема щодо оплати цифрових товарів |
| Microsoft Store | PWA, запакована через PWABuilder | Кожен деплой | Охоплює лише Windows |
| Вебкод у нативній оболонці (Capacitor, Cordova) | Ваш вебкод, упакований у застосунок, що показує його у WebView, із плагінами для нативних можливостей | Через рев’ю магазинів; вебкод можна оновлювати «по повітрю» в межах правил магазинів | Apple відхиляє застосунок, що є лише «перепакованим сайтом» (пункт 4.2); тепер ви ще й підтримуєте застосунок |
Пункт 4.2 правил Apple — та лінія, що вирішує останній рядок: застосунок має містити можливості, вміст та інтерфейс, що «підносять його над перепакованим сайтом», а той, що не корисний, не унікальний і не схожий на застосунок, у магазині не місце. Оболонка навколо вашого сайту без нічого нативного всередині — саме те, що це правило відхиляє. На Android навпаки: Trusted Web Activity — підтримуваний спосіб покласти сайт у Google Play, якщо Digital Asset Links на вашому сайті доводять, що застосунок і сайт належать одному розробникові. Саме рішення випливає з таблиці можливостей більше, ніж зі смаку:
| Якщо продукт… | Будуйте |
|---|---|
| Потребує лише рядків 1–7, і його знаходять пошуком чи посиланнями — контент, комерція, бронювання, дашборди, внутрішні інструменти | PWA |
| Потребує рядків 8–11 на iPhone — push, лічильників | PWA зі спроєктованим кроком встановлення — або застосунок, якщо користувачі не встановлюватимуть |
| Потребує картки в Google Play, але нічого поза вебом | PWA плюс Trusted Web Activity |
| Потребує рядків 12–16 на iPhone або роботи, коли застосунок закрито | Нативний або кросплатформний застосунок — див. сторінки про React Native, Flutter, iOS та Android |
| Є грою, камерою, медіазастосунком чи будь-чим, де важить кожен кадр | Застосунок |
П’ять кейсів і те, чого в кожному бракувало
Три з п’яти кейсів — про платформу під вебзастосунком: виробник браузера чи операційна система змінює правило, — а два — про вибір самої команди. Кожен задокументували ті, кого він стосувався.
| Кейс | Що сталося | Чого бракувало |
|---|---|---|
| Create React App, 2017–2018 | Найпопулярніший стартовий шаблон React зробив кожен новий застосунок типово офлайн-першим; за півтора року service worker зробили необов’язковим | Процесу оновлення: service worker — це рішення про деплой, а не налаштування шаблону |
| Сім днів Safari, березень 2020 | Safari почав видаляти все, що зберегли скрипти сайту, після семи днів користування без візиту | Сервера зі справжньою копією; чернетки й офлайн-дані, що жили лише в браузері, пропали |
| Pinterest, 2017–2018 | Після ставки лише на нативні застосунки компанія за три місяці перебудувала мобільний сайт як PWA; реєстрації за рік зросли на 843% | До того: швидкого мобільного вебу для людей, що приходять за посиланням |
| Apple і ЄС, лют.–бер. 2024 | Бета-версії iOS 17.4 перетворили застосунки на домашньому екрані в ЄС на ярлики Safari; 1 березня Apple це скасувала | Плану на випадок, коли платформа вимикає режим встановлення, а з ним push і лічильники |
| Запити на сповіщення, 2020–2025 | Chrome приглушив запити для сайтів із низьким рівнем згоди, а потім почав забирати невикористані дозволи — встановлені вебзастосунки виняток | Запиту в мить, коли користувачеві щось потрібно, а не під час першого візиту |
Перший кейс показує, як поширюється проблема оновлення. Create React App 1.0, що вийшов 18 травня 2017 року, зробив кожен новий проєкт типово прогресивним вебзастосунком: service worker кешував усю збірку й віддавав її за принципом «спершу кеш». Через десять днів з’явився issue під назвою «Why was service worker merged into create react app?», і в понад 154 коментарях розробники описували те саме. Вони деплоїли, а їхні користувачі далі бачили старий сайт. Версія 2.0 від 1 жовтня 2018 року зробила service worker необов’язковим. Документація прямо назвала причину: офлайн-перші застосунки можуть ускладнити налагодження деплоїв, а користувачі бачать старіший вміст, доки не закриють усі відкриті вкладки, — «перезавантаження замало». У воркері нічого не було зламано. Бракувало процесу оновлення з розділу вище, а шаблон не може знати, коли вашим користувачам слід отримати нову версію.
Інші три кейси — про правила, якими команда не володіє. У березні 2020 року Safari почав видаляти все сховище сайту, записане скриптами, — IndexedDB, localStorage, реєстрації service worker і кеші — після семи днів користування Safari без візиту. Це було спрямовано проти трекерів, і застосунки на домашньому екрані стали винятком, але будь-який вебзастосунок, що тримав єдину копію чогось користувацького в браузері, її втратив. У лютому 2024 року бета-версії iOS 17.4 перетворили вебзастосунки на домашньому екрані в ЄС на ярлики, що відкривалися у вкладці Safari, — без власного вікна, push і лічильників; Apple послалася на Акт про цифрові ринки ЄС. Після хвилі обурення розробників і на тлі уваги Європейської комісії Apple скасувала зміну 1 березня. А браузери поступово зачиняють двері для недбалого push: Chrome 80 2020 року приглушив запит для сайтів, чиї запити користувачі зазвичай відхиляють, а з жовтня 2025 року Chrome забирає дозвіл на сповіщення в сайтів, які користувач більше не відвідує, зазначивши, що взаємодію отримує менше 1% сповіщень. Встановлені вебзастосунки на друге правило не підпадають. Усі три кажуть одне: вебзастосунок працює за правилами, які встановлює хтось інший, тож він тримає дані на власному сервері, має другий канал для всього критичного й заробляє кожен дозвіл у мить, коли той потрібен.
Де це вироджується
PWA відмовляє так, що з машини розробника цього не видно: там швидка мережа, браузер — Chrome, а вкладка завжди свіжа. Майже кожен збій — одне з цього:
- Немає процесу оновлення — випущене виправлення чекає за старим воркером, доки відкрита вкладка чи застосунок на домашньому екрані.
- Перейменований файл service worker, через який зламаний лишається зареєстрованим і далі відповідає.
index.htmlбез хешу, який віддають за принципом «спершу кеш», закріплюючи користувачів на одному релізі.- Дані API з кешу, показані як актуальні, без часу отримання.
- Офлайн-записи, які повторюють без ключа ідемпотентності, — і одне замовлення стає двома.
- Єдина копія даних користувача у сховищі браузера, яке браузер має право видалити (рядок 2).
- Закешовані непрозорі відповіді, що ховають помилки й з’їдають квоту по сім мегабайтів за раз.
- Запит на сповіщення під час першого візиту — один раз відхилений і приглушений назавжди.
- Фіча, збудована на рядках 8–16, відсутність якої на iPhone виявили після затвердження дизайну.
- Обгортка для магазину без нічого нативного, відхилена за пунктом 4.2.
Чесна межа в тому, що PWA живе всередині браузера, а браузер належить комусь іншому. На Android і десктопі Chromium дає вебзастосунку майже все, що отримує застосунок, і розрив далі скорочується. На iPhone веб отримує те, що вирішить Apple, рішення встигало змінитися в обидва боки за один цикл бета-версій, а рушій під ним ви не обираєте. Це причина будувати PWA з розплющеними очима, а не причина її уникати. Для більшості продуктів, що живуть із того, що їх знаходять і поширюють, посилання, яке працює одразу, краще за застосунок, який спершу треба встановити, — якщо нічого суттєвого не залежить від можливості, якої одна з платформ не дає.
Практична перевірка займає годину. Випишіть кожну можливість, потрібну продукту, і знайдіть її рядок у таблиці вгорі. Відкрийте сайт на iPhone, додайте його на домашній екран і перевірте, що там працюють вхід, push і офлайн. Задеплойте помітну зміну, лишивши вкладку відкритою, і подивіться, коли вона доїде. Знайдіть назву файлу service worker і переконайтеся, що вже сьогодні за цією адресою можна задеплоїти заміну, яка нічого не робить. Пошукайте в правилах кешування щось персональне або щось непрозоре. Вимкніть мережу, зробіть зміну, двічі ввімкніть зв’язок і порахуйте, скільки разів її отримав сервер.
Коли це застосовувати
Застосовуйте, коли
- Продукти, які по суті є сайтом і виграють від офлайну та місця на домашньому екрані.
- Усе, що знаходять пошуком або передають посиланням, де крок встановлення коштує вам користувачів.
- Внутрішні інструменти й польові застосунки на ваших пристроях, де рев’ю в магазині — чисті накладні витрати.
- Одна кодова база на десктоп і мобільні від команди, яка не може забезпечити людьми дві нативні платформи.
Уникайте, коли
- Усе, що потребує Bluetooth, NFC, USB чи послідовного порту на iPhone (рядок 15).
- Продукти, що залежать від роботи, поки застосунок закрито (рядки 12–14).
- Продукти, чиї користувачі шукають застосунки, гортаючи магазин, — туди посилання не доходить.
- Ігри, камера, медіазастосунки й усе інше, де браузер — хибне середовище для кожного кадру.
Джерела й метод
На чому стоїть ця стаття
- 17 джерел
- 1 задокументований збій
- Перевірено 13 жовтня 2026 р.
Практики й походження термінів
таблиця можливостей, сховище, service worker і встановлення- Документаціяmdn/browser-compat-data (відкривається в новій вкладці)GitHub · MDN
- ДокументаціяStorage quotas and eviction criteria (відкривається в новій вкладці)MDN Web Docs
- ГайдThe service worker lifecycle (відкривається в новій вкладці)web.dev
- ГайдRemoving buggy service workers (відкривається в новій вкладці)Chrome for Developers
- ГайдWhat does it take to be installable? (відкривається в новій вкладці)web.dev
- СтаттяWeb Push for Web Apps on iOS and iPadOS (відкривається в новій вкладці)WebKit · 16.02.2023
- СтаттяMeet Declarative Web Push (відкривається в новій вкладці)WebKit · 27.03.2025
- СтаттяWebKit Features in Safari 26.0 (відкривається в новій вкладці)WebKit · 15.09.2025
- ДокументаціяApp Review Guidelines (відкривається в новій вкладці)Apple Developer
Збої, на яких вчиться стаття
типове налаштування Create React App у «П’ять кейсів і те, чого в кожному бракувало»- Документаціяcreate-react-app — CHANGELOG 1.x (відкривається в новій вкладці)GitHub
- ДокументаціяWhy was service worker merged into create react app? — #2398 (відкривається в новій вкладці)GitHub · 28.05.2017
- СтаттяCreate React App 2.0: Babel 7, Sass, and More (відкривається в новій вкладці)React Blog · 01.10.2018
Safari, Pinterest, ЄС і запити на сповіщення
чотири інші кейси- СтаттяFull Third-Party Cookie Blocking and More (відкривається в новій вкладці)WebKit · 24.03.2020
- СтаттяZack Argyle — A one year PWA retrospective (відкривається в новій вкладці)Pinterest Engineering · 20.07.2018
- СтаттяiOS 17.4 won’t remove Home Screen web apps in the EU after all (відкривається в новій вкладці)9to5Mac · 01.03.2024
- СтаттяIntroducing quieter permission UI for notifications (відкривається в новій вкладці)Chromium Blog · 07.01.2020
- СтаттяGoogle Chrome silences those pesky notifications (відкривається в новій вкладці)TechCrunch · 10.10.2025
Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.
Було корисно?
Поділіться з тим, хто працює над тією ж задачею.