To turn your side project into an installable web app, add a web app manifest with a name, a start URL, a display mode and two icons (192 and 512 pixels), serve the site over HTTPS, and add a small service worker that shows an offline page when the network drops. That is a PWA, short for progressive web app. People install it from the browser, it gets its own icon and window, and you skip app stores and a second codebase. For most web side projects whose users keep asking "is there an app?", that is the honest answer.
This guide is for people with a day job and a web side project that gets used on phones. Every browser rule below comes from current Chrome, MDN, WebKit and Apple docs, checked on 4 November 2026. The worked example near the end is made up.
Do you need a native app, a PWA or just a bookmark?
A PWA is worth a weekend when people come back often, mostly on phones, and need nothing a browser cannot give them. A quick fit table:
Your situation | Best fit | Why |
|---|---|---|
People open it several times a week, often on a phone (lists, trackers, timers, dashboards) | PWA | An icon and its own window remove the "find the tab" step, which is most of the friction. |
People visit once a month to look something up | Plain website | Nobody installs a tool they rarely use. Spend the time on speed instead. |
Users need Bluetooth or USB devices on iPhone | Native app | Safari does not support Web Bluetooth or WebUSB. |
Your audience searches the app stores first | Native app, or a store wrapper later | Being findable where they look beats a perfect install button. |
You are not sure yet | PWA first | It reuses your existing site, so it is the cheapest test of whether "an app" changes behaviour. |
If you land in the native column, the SideHunt guide to shipping your first mobile app as a side project covers that path.
What does "installable" actually mean in 2026?
Installable means a browser will offer to add your site as an app, with its own icon on the home screen or in the app launcher. Chrome's install criteria page lists what triggers its install promotion and the beforeinstallprompt event:
The site is served over HTTPS.
It links a manifest with
nameorshort_name,iconsincluding a 192 pixel and a 512 pixel icon,start_url, anddisplayset tostandalone,fullscreen,minimal-uiorwindow-controls-overlay.prefer_related_applicationsis missing or false.The user has clicked or tapped the page at least once and spent at least 30 seconds on it, and the app is not already installed.
Notice what is missing: a service worker. Chrome's post on revisiting its installability criteria says that requirement was removed for installing from the browser menu in Chrome 108 on mobile and Chrome 112 on desktop, and Chrome now shows a default offline page for apps without their own. The same post said the automatic prompt still looked for a fetch handler, and Chrome also uses a machine learning model to time install prompts on Android. So when a prompt appears is Chrome's call, not yours.
Other browsers differ. MDN's guide to making PWAs installable notes that Firefox on desktop does not install PWAs from a manifest, while Safari on macOS lets people use File, then Add to Dock, for any site. The manifest turns "possible if the user digs through menus" into "offered by the browser".
How do you write a web app manifest?
A web app manifest is a small JSON file that tells the browser your app's name, icons, start page and how it should open. Link it from the head of every page with a rel="manifest" link tag pointing to a file like manifest.webmanifest. MDN's web app manifest reference lists every member. These matter on day one:
Field | Example value | What it does |
|---|---|---|
|
| Full name in the install dialog and app settings. |
|
| Label under the home screen icon. Keep it short enough not to be cut off. |
|
| Where the app opens. Point it at the useful screen, not your marketing page. |
|
| Opens in its own window without the address bar. |
| 192 and 512 pixel PNGs | Home screen, launcher and splash icons. |
|
| A stable identity, so changing |
|
| Title bar colour and the splash screen background some platforms generate. |
Add one more icon with "purpose": "maskable". Android crops icons into circles and other shapes, and the guide to maskable icons explains the safe zone: keep your logo inside a centred circle whose radius is 40% of the icon width, on a solid background. It also recommends a separate maskable file rather than one icon marked as both, because the padding makes the normal icon look small.

Can a PWA work on iPhone and iPad?
Yes, and since iOS 26 it is simpler than it used to be. People open your site in Safari, tap Share, then Add to Home Screen. Apple's guide to turning a website into an app shows an Open as Web App switch in that sheet, and notes that people may need to tap Edit Actions to find Add to Home Screen at all.
WebKit's Safari 26 features post explains the bigger change: on iOS 26 and iPadOS 26, every site added to the Home Screen opens as a web app by default, manifest or not. In WebKit's words, there are now "zero requirements" for installability in Safari. Safari still uses your manifest's name and icons, but it is no longer a gate.
Two iPhone realities to plan around:
There is no install prompt API. MDN states that
beforeinstallpromptis not supported on iOS, so show a short hint instead: "Tap Share, then Add to Home Screen."Web push only works after install. WebKit added web push for Home Screen web apps in iOS 16.4, and the permission request must follow a user action like a tap.
What is a service worker, in plain words?
A service worker is a small JavaScript file the browser runs in the background, sitting between your pages and the network. When a page asks for something, the service worker can answer from a local cache, go to the network, or both. That is how a PWA shows something useful in a train tunnel instead of the browser's error page. The Service Worker API docs on MDN are the reference. Three rules save hours later:
HTTPS only. Service workers run only in secure contexts. Localhost counts as secure for development.
Put the file at your site root. A worker controls pages under the folder it is served from, so
/sw.jscovers the whole site while/js/sw.jsonly covers/js/.Never rename it. The lifecycle guide below says to avoid changing the worker URL. Change the code inside instead.
What is the minimum safe service worker?
For a side project, the safe version caches very little. Its job is an offline page and a faster shell, not a full offline copy. Following the guide to an offline fallback page and MDN's PWA caching guide, aim for this:
On install, precache a few files: your offline page, the CSS and JavaScript it needs, and your icons. Version the cache name, like
shell-v3.For page requests, go network first. If the network fails, show the offline page.
For files with a hash in the name, go cache first. A file like
app-4f9c2.jsnever changes, so the cache is safe and fast.Leave API calls and logged-in pages alone. A cached response can show yesterday's data, or one person's data to another on a shared device.
On activate, delete caches you no longer use.
Full offline editing with sync and conflict handling is a much bigger project, so leave it until users ask.
How do you avoid users getting stuck on an old version?
By default, a new worker installs and then waits until no tab is using the old one. The deep dive on the service worker lifecycle notes that even a refresh is not enough, because the old page stays open while the new one loads. People who keep your app open for days stay on old code for days.
There are two escape hatches, and both have costs. skipWaiting() makes the new worker take over as soon as it installs, but it may then control pages loaded with the old version, so new code that expects new HTML breaks mid-session. clients.claim() lets a new worker control pages that are already open, which the same guide calls mainly a first-load concern.
The calm middle path: when a new worker is waiting, show a small "New version available. Refresh" banner, and call skipWaiting() only after the user taps it. Bump your cache name every release so old files get cleaned up. Chrome ignores HTTP caching headers when checking the worker script for updates. Caching headers for the rest of your site are covered in the SideHunt guide to getting a side project ready for a traffic spike.
Should you write it by hand or use a library?
Write the first one by hand if your site is small. Reach for a library when you have a build step and many hashed files to precache:
Workbox, the Chrome team's service worker libraries with ready-made caching strategies. Last released in May 2026.
Vite PWA plugin for Vite projects. It injects the manifest, generates a Workbox worker and has a built-in prompt for new content. Version 2 shipped in October 2026.
Serwist, a Workbox-style toolkit with Next.js support, updated in October 2026. The Next.js PWA guide shows a built-in manifest file and names Serwist as one option for offline caching.
One warning: Serwist's quick-start example turns on both skipWaiting and clientsClaim. That suits a static site and is risky for an app with long logged-in sessions. Read the defaults before you ship them.

When should you show your own install button?
Only after someone has had a good moment with your product, and only where it works. The beforeinstallprompt event fires in Chromium browsers like Chrome, Edge and Samsung Internet, not in Firefox or Safari. The pattern: listen for the event, call preventDefault(), keep the event, reveal your hidden button, and call prompt() when the user taps it.
The guide to promoting PWA installation sets the rules: keep promotion out of the main flow, let people dismiss it, remember that choice, and only show it after the event fires. Without preventDefault(), Chrome on Android may show its own mini infobar.
Weak | Better |
|---|---|
Full-screen "Install our app!" modal on the first visit | A small button after the user finishes their first list, timer or report |
"Install app" | "Add Pantrytide to your home screen to open your list in one tap" |
Button visible in every browser, even when nothing will happen | Hidden by default, shown only after |
Asking again on every visit after a dismissal | Remember "not now" and ask again only after something changes, like signing up |
Same button on iPhone | A one-line iOS hint: "Tap Share, then Add to Home Screen" |
How do you test a PWA before you tell anyone?
Use Chrome DevTools, then a real phone. The DevTools guide to debugging PWAs covers the Application panel:
Manifest: the parsed manifest, icon previews with a minimum safe area check for maskable icons, and an Installability section listing errors.
Service workers: go offline, force an update and skip waiting by hand.
Cache storage: exactly what you cached. Account pages there mean a bug.
Clear storage: reset everything to test a first visit again.
Do not look for a PWA score in Lighthouse. The Lighthouse 12 release notes say the PWA category was removed in April 2024 and point to DevTools instead. Then install the app on your own phone, turn on airplane mode and open it from the icon. If you see your offline page, the basics work.
How do you know if anyone installs it?
Tag sessions by how the app was opened. The chapter on detecting PWA usage shows the approach: check matchMedia("(display-mode: standalone)") on load and send the result with your page view. Chromium browsers also fire appinstalled when someone accepts the install, a good moment to hide your button and log it. Safari and Firefox do not fire it, so standalone sessions are your cross-browser number. Then compare one thing: do installed users come back more often? If you have no analytics yet, see the SideHunt guide to analytics for a side project you will actually check.
What can a PWA still not do well?
Some hardware access. Web Bluetooth and WebUSB are marked experimental and are missing from Safari and Firefox, so not on iPhone at all.
Background work. Background Sync is not supported in Safari or Firefox. Sync when the app opens.
iPhone discovery. No install prompt, a manual install, and push only after install.
Store presence. People who only search app stores will not find you without a wrapper.
Which traps catch first-time PWA builders?
Caching logged-in pages or API responses cache first, then seeing stale or wrong data.
Forgetting to precache the offline page itself.
Pointing
start_urlat the marketing homepage instead of the app.Using one icon with transparent edges, which Android shows inside a white circle.
A made-up example: Hana and Pantrytide
Hana, a made-up QA engineer, runs Pantrytide, a made-up shared shopping list for flatmates. Around 140 people use it, mostly on phones in shops, and three emails in one month ask for "the app".
On Saturday she adds a manifest with "display": "standalone" and start_url set to the list screen, makes a 192, a 512 and a maskable icon, and clears the Installability errors in DevTools. On Sunday she writes a 60-line service worker: precached offline page and CSS, network first for pages, hashed scripts cache first, nothing from the API cached.
Her install button appears only after someone ticks off five items; iPhone users see the Share hint. A month later, in this made-up story, a third of weekly users open Pantrytide from the icon, and they come back on more days than browser users. The app emails stop.
A one-weekend plan
When | Task | Done when |
|---|---|---|
Saturday morning | Write the manifest, link it on every page, make normal and maskable icons | DevTools shows no install errors |
Saturday afternoon | Install on desktop Chrome and your phone | The app opens on the screen you chose, in its own window |
Sunday morning | Service worker: offline page, network-first pages, versioned cache, cleanup | Airplane mode shows your offline page |
Sunday afternoon | Update banner, install button and iOS hint | The button appears only after a real action |
Sunday evening | Tag standalone sessions and log installs | Installed sessions show in your analytics |
Timings are suggestions. An app with logins needs more care on the service worker.
FAQ
Do I need a service worker for a PWA?
Not to be installable in current Chrome or Safari. Chrome dropped that requirement for menu installs in versions 108 and 112, and Home Screen web apps on iPhone never required one. You still want a small one for an offline page and faster loads.
Can a PWA work on iPhone?
Yes. People add it with Share, then Add to Home Screen, and since iOS 26 it opens as a web app by default. There is no install prompt API, and web push only works once the app is on the Home Screen.
Can I put a PWA in the Play Store?
Yes. A Trusted Web Activity is a thin Android app that opens your PWA full screen, and the Bubblewrap tool generates one. You then go through normal Play Store publishing. Start with web install and add a listing only if people ask.
Will a PWA hurt SEO?
Not by itself. It is still your website on the same URLs. Google's guide to fixing JavaScript search problems says its renderer clears storage and cookies between page loads, so make sure every page works on a first visit without cached data, and use real links with href.
Make it installable, then make it worth opening
To turn a website into an app people keep, ship the manifest and icons first, add a small, careful service worker, and ask for the install only after someone gets value. And when your side project is ready for people who browse new tools on purpose, list it on SideHunt.