PWA development turns a website into a progressive web app: an application that loads instantly, keeps working on a bad connection, can be installed on the home screen and sends push notifications – all from one web codebase, without an app store standing between you and your users. For many businesses it is the shortest path to an “app” that customers actually use, at a fraction of the cost of building and maintaining separate iOS and Android apps.
Contents
At CodeWiz we build progressive web apps from scratch and retrofit PWA capabilities into existing websites, online stores and internal tools. We design the caching strategy, write and test the service worker, wire up web push on the backend, polish the installed experience and make sure everything behaves correctly on Android, iOS and desktop browsers – including the parts where those platforms still differ.
This service is for e-commerce shops whose mobile visitors abandon slow pages, field teams and warehouses that work with patchy connectivity, publishers and SaaS products that want re-engagement without an app store listing, and companies that need an internal app on every employee’s phone without device management headaches.

Offline Functionality with Service Workers and Smart Caching
The heart of every PWA is the service worker – a script the browser runs in the background, between your app and the network. It intercepts requests and decides, request by request, whether to answer from a local cache, from the network or from both. Done well, repeat visits load in milliseconds and the app stays usable on a train or in a warehouse with one bar of signal. Done badly, users get stuck on stale versions of your site.
Choosing a caching strategy per type of content
| Strategy | How it works | Best for | Trade-off |
|---|---|---|---|
| Precache | Files are downloaded when the service worker installs | App shell: core HTML, CSS, JavaScript, fonts, offline page | Every change needs a new build manifest and update flow |
| Cache first | Serve from cache, fall back to the network | Versioned assets, images, icons | Needs expiration rules or the cache grows without limit |
| Network first | Try the network, fall back to cache after a timeout | HTML pages, account data, prices | Slower than cache on flaky networks |
| Stale-while-revalidate | Serve the cached copy instantly, refresh it in the background | Catalogue data, blog lists, settings | Users may see slightly outdated data for one visit |
| Network only + background sync | Send to the server; queue and retry if offline | Form submissions, orders, field reports | Requires idempotent endpoints on the backend |
What a production service worker looks like
We usually build service workers with Workbox, either through vite-plugin-pwa, the Angular service worker or a custom build step. A condensed example of the routing we use:
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst, CacheFirst, NetworkOnly } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { BackgroundSyncPlugin } from 'workbox-background-sync';
precacheAndRoute(self.__WB_MANIFEST);
registerRoute(({ request }) => request.mode === 'navigate',
new NetworkFirst({ cacheName: 'pages', networkTimeoutSeconds: 3 }));
registerRoute(({ request }) => request.destination === 'image',
new CacheFirst({ cacheName: 'images', plugins: [
new ExpirationPlugin({ maxEntries: 200, maxAgeSeconds: 30 * 86400 })] }));
registerRoute(({ url }) => url.pathname === '/api/orders',
new NetworkOnly({ plugins: [new BackgroundSyncPlugin('orders')] }), 'POST');Offline data, not just offline pages
For apps that must create data offline – inspection checklists, delivery confirmations, draft orders – we store records in IndexedDB (through a small wrapper such as Dexie), mark them as pending and sync them when the connection returns. Background Sync handles this in Chromium browsers; on Safari and Firefox we sync on the next launch or online event. Client-generated IDs ensure a retried request never creates a duplicate. It is the same principle we described in our article on zero data loss e-commerce: every customer action is captured and recoverable.
Web Push Notifications That Users Actually Want
Push notifications bring users back without email or an app store. A PWA subscribes through the Push API, your server signs messages with VAPID keys, and the browser vendor’s push service delivers them even when the app is closed. Web push works on Android, on desktop Chrome, Edge, Firefox and Safari, and on iPhone and iPad from iOS 16.4 – but on iOS only after the user has added the app to the home screen.
Permission strategy matters more than the code
The fastest way to lose the push channel is to ask for permission on the first page load. Browsers increasingly hide or auto-block such prompts, and a “Block” decision is very hard to reverse. We ask in context instead: after a user places an order (“Notify me when it ships”), saves a search or subscribes to a topic. A soft prompt in your own UI explains the value first, and only a “Yes” triggers the browser dialog.
Backend and delivery
import webpush from 'web-push';
webpush.setVapidDetails('mailto:hello@example.com',
process.env.VAPID_PUBLIC_KEY, process.env.VAPID_PRIVATE_KEY);
await webpush.sendNotification(subscription,
JSON.stringify({ title: 'Your order has shipped', url: '/account/orders/1042' }),
{ TTL: 3600, urgency: 'high' });- Subscriptions are stored per user and device in your database; responses with status 404 or 410 remove expired ones automatically.
- Laravel projects use the
laravel-notification-channels/webpushpackage, Node.js projects theweb-pushlibrary; Firebase Cloud Messaging is an option when you also have native apps. - Sending goes through a queue, so a campaign to many subscribers never blocks your web requests.
- Payloads carry a deep link, a tag to replace outdated notifications, and the service worker opens or focuses the right screen on click.

App-Like Experience: Installability, Manifest and Native Feel
An app-like experience is what separates a PWA from a website with a service worker. Users should be able to install it, launch it from the home screen without browser chrome, and feel no difference from a native app in day-to-day use.
The web app manifest
{
"name": "Example Store",
"short_name": "Store",
"id": "/",
"start_url": "/?source=pwa",
"display": "standalone",
"theme_color": "#0f172a",
"background_color": "#ffffff",
"icons": [
{ "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/maskable-512.png", "sizes": "512x512",
"type": "image/png", "purpose": "maskable" }
],
"shortcuts": [{ "name": "My orders", "url": "/account/orders" }]
}Details that make it feel native
- An app shell that renders immediately from cache, with skeleton screens instead of blank pages while data loads.
- A custom install button driven by the
beforeinstallpromptevent on Chromium, and a short visual guide for “Add to Home Screen” on iOS, where no install prompt exists. - Maskable icons, a matching theme colour, safe-area handling for notched screens and a proper splash screen.
- Smooth navigation with the View Transitions API, touch targets of at least 44–48 px and no hover-only interactions.
- App shortcuts, the Badging API for unread counts and the Web Share API where they add real value.
- A clear update flow: when a new version is deployed, users see “New version available – refresh” instead of silently running old code.
Cross-Platform Compatibility: Android, iOS and Desktop
One codebase is the main economic argument for a PWA, but “works everywhere” needs testing, because platforms support different parts of the PWA stack. We build with progressive enhancement: the core experience works in every modern browser, and extra capabilities switch on where they are available.
| Capability | Android (Chrome, Edge, Samsung Internet) | iOS and iPadOS (Safari) | Desktop Chrome and Edge | Desktop Firefox |
|---|---|---|---|---|
| Offline via service worker | Yes | Yes | Yes | Yes |
| Install to home screen or desktop | Yes, with install prompt | Manually via Share menu | Yes, with install prompt | Limited |
| Web push | Yes | iOS 16.4+, installed apps only | Yes | Yes |
| Background Sync | Yes | No | Yes | No |
| Store distribution | Google Play via Trusted Web Activity | App Store only with a native wrapper | Microsoft Store via PWABuilder | – |
We test on real Android and iOS devices, not only emulators, and keep an eye on storage limits: Safari can evict data of sites that are not used for a while, so anything important is always synced to the server. If you need a Google Play listing, we package the PWA as a Trusted Web Activity with Bubblewrap and Digital Asset Links; for the Apple App Store, a thin Capacitor wrapper with genuine native features is usually required to pass review.
PWA vs Native App vs Responsive Website
A PWA is not always the right answer, and we will say so. The table below is the starting point we use with clients:
| Criterion | Responsive website | Progressive web app | Native or cross-platform app |
|---|---|---|---|
| Codebases to maintain | One | One | One to three, plus the web |
| Discoverable in search engines | Yes | Yes | Only the store listing |
| Offline use and install | No | Yes | Yes |
| Updates | Instant | Instant, no store review | Through store review |
| Deep hardware access (Bluetooth on iOS, NFC, background location) | No | Partial, mostly on Android | Full |
| Typical budget | Lowest | Moderate | Highest |
If your users need heavy background processing or deep integration with iOS hardware, a native or cross-platform app is the better tool. For catalogues, booking, ordering, dashboards and most internal tools, a PWA covers the requirements at a much lower total cost.
Performance, Security and SEO for Progressive Web Apps
A PWA must be served over HTTPS, and it only feels like an app if it is fast. We treat performance as a requirement from the first sprint: server-side rendering or static generation for the first view, code split by route, optimized images and a strict performance budget checked in CI. The techniques overlap with how we approach 100/100 page vitals, and an edge layer such as a well-configured Cloudflare setup keeps the first load fast for visitors far from your server.
- SEO: content is rendered on the server or at build time, so search engines index real HTML instead of an empty JavaScript shell. Each screen keeps its own URL.
- Security: a strict Content-Security-Policy, the service worker scope limited to what it needs, and authenticated API responses never cached in shared caches.
- Auditing: Lighthouse dropped its dedicated PWA category in version 12, so we verify installability and the service worker in Chrome DevTools, and test offline behaviour with network throttling and airplane mode on real devices.
Turning an Existing Website or Store into a PWA
You rarely need a rewrite. Most frameworks have mature PWA tooling, and we add PWA capabilities incrementally:
- Vue, React, Svelte and Vite projects:
vite-plugin-pwawith a custom Workbox service worker. - Next.js and Nuxt: Serwist or the Nuxt PWA module, with careful handling of server-rendered routes.
- Angular: the built-in
@angular/service-workerand its data group configuration. - Laravel and plain PHP: a manifest, a hand-written or Workbox-built service worker and web push through Laravel notifications.
- WordPress and WooCommerce: a lightweight service worker that caches assets and an offline page, while cart, checkout and account pages always go to the network.
Common PWA Mistakes We Fix
- Caching HTML with cache first, so users stay on an old version for days after a deployment.
- Calling
skipWaiting()unconditionally, which swaps code under an open tab and breaks lazily loaded chunks. - Caching personalised API responses, so one user can see another user’s data on a shared device.
- Asking for notification permission on the first visit, and losing the channel for good.
- No expiration rules, so caches of images and opaque cross-origin responses fill the storage quota.
- Testing only in Chrome on desktop and discovering on launch day that iOS behaves differently.
What Drives the Cost of PWA Development
We estimate after a short discovery rather than quoting blindly. The biggest cost drivers are:
- Whether we build a new application or add PWA features to an existing site.
- The depth of offline support: cached reading is simple, offline data entry with conflict resolution is not.
- Push notification logic, segmentation and the admin interface for sending campaigns.
- The number of platforms and devices to test, and whether store packaging is required.
- Integrations with your backend, ERP, CRM or payment provider.

How We Work
- Discovery: we map your users, devices and connectivity, and agree which screens must work offline and which events deserve a notification.
- Plan: we define the caching strategy per route, the data sync model, the push architecture and the browser support matrix.
- Build: we develop the app shell, service worker, manifest, offline storage and push backend, with a performance budget enforced in CI.
- Test: we test on real Android and iOS devices and desktop browsers, offline and on throttled networks, including update and rollback scenarios.
- Launch: we deploy behind HTTPS and a CDN, roll out the service worker carefully and monitor errors and install rates.
- Support: we keep dependencies and browser compatibility up to date and evolve the app with you.
Why CodeWiz
- Full-stack ownership: we build the frontend, service worker, APIs, push backend and servers, so offline sync and notifications work end to end.
- Performance specialists: fast loading is our everyday work, from code to Varnish and Cloudflare.
- Honest recommendations: if a responsive website is enough, or you truly need a native app, we will tell you.
- Open-source first: Workbox, Vite, Dexie and open web standards keep you free of vendor lock-in.
- Fair pricing: premium development without premium prices, from Banja Luka, working remotely with clients locally and internationally – read more about CodeWiz.
Frequently Asked Questions
How much does PWA development cost?
It depends on whether we start from scratch or extend an existing site, how much has to work offline and how complex the notification logic is. Adding installability and offline caching to an existing website is a modest project; a full offline-first application with data sync is considerably larger. You get a clear estimate after discovery.
Do progressive web apps work on iPhone?
Yes. Offline support, installation via “Add to Home Screen” and, from iOS 16.4, web push for installed apps all work on iPhone and iPad. Some APIs, such as Background Sync, are not available in Safari, so we design fallbacks for them.
Can a PWA be published on Google Play or the App Store?
Google Play accepts PWAs packaged as a Trusted Web Activity, and the Microsoft Store accepts them too. The Apple App Store requires a native wrapper and features that go beyond a website, so we evaluate that case individually.
Will a PWA hurt my SEO?
Not when it is built correctly. With server-side rendering or static generation, every page is indexable and fast, which helps rankings. Problems only appear with client-only rendering that shows search engines an empty page.
How long does it take to build a PWA?
Adding PWA features to an existing website usually takes a few weeks including testing. A new application with offline data entry and push notifications typically takes a few months, depending on scope and integrations.
Ready to Build Your Progressive Web App?
Tell us who your users are and where your current website or app falls short, and we will propose a PWA plan that fits your goals and budget. Contact us to discuss your project, or see the full scope of our PWA development services.