Guides explain one thing at a time. This one follows a single, realistic site through every decision, with the reasoning. The site: a neighbourhood restaurant's twelve-page website on ordinary shared hosting — menu, opening hours, location, gallery, an online ordering page provided by a third-party service, and a news page used twice a year. WordPress, one plugin for the menu, an SSL certificate that came with the hosting.
Hour 0: is this worth doing?
The owner's question was "should I get an app?" — meaning pay someone thousands for a native app. The honest analysis:
- Regulars return weekly to check the menu and order. Return visits: yes.
- Nothing the app needs is beyond the web: show pages, link to ordering, send an occasional notification.
- Half the customers are on iPhone; a native Android app alone would miss them.
- Budget: near zero.
Verdict: installable web app, no native build. Possibly a Google Play listing later if customers ask "is it on the Play Store?".
Hour 1: fix the site first
Opened on a phone, the site had two problems that would become the app's problems: a hero image of 3.2 MB and a "reservations" link to a page that had been removed (404). Compressed the image to 180 KB, fixed the link. Checked that every page loads over HTTPS with no mixed-content warnings — one gallery image was hard-coded http://; fixed. Total: twenty minutes, and the site got faster for everyone, app or not.
Hour 1.5: assets
- Icon. The logo is a wide wordmark — useless as an icon. Used the initial letter in the brand colour on a cream background, exported at 1024×1024, and ran it through the icon resizer checking the maskable preview. The letter stayed clear of the circle.
- Colours. Palette extractor on the logo: a deep red (
#8B1E1E) fortheme_color— white text reads fine on it — and the site's cream background (#FBF7EF) forbackground_color. - Names.
name: "Noodle House Camden".short_name: "Noodle House" — 12 characters, fits under the icon.
Hour 2: generate and deploy
In the PWA Builder: URL, names, colours, icon. Caching mode: Balanced — the menu changes seasonally, prices occasionally, and stale-for-one-visit is acceptable, while the ordering page must always be live. Left OneSignal blank for now.
Downloaded the zip. Uploaded manifest.json, sw.js, offline.html and icons/ to public_html via the host's file manager. Added the head snippet with a snippet plugin. Opened /sw.js in a browser — JavaScript, good. Opened /manifest.json — JSON, good.
One edit to sw.js: the ordering page is a third-party page embedded in an iframe from another domain, so nothing to exclude there; but the WordPress admin paths were added to the never-cache list as the WordPress guide recommends.
On a phone: Chrome offered Install from the menu immediately. Installed. Opened from the icon: cream splash, red status bar, menu page, no browser bar. Aeroplane mode: menu and hours (visited) loaded; gallery (not visited) showed the cream offline page with the logo. Exactly as intended.
Hour 3: the install page and the tables
Created /app following the install page pattern: icon, "Menu, hours and ordering — on your home screen, even with no signal downstairs", an Install button for Android, the three share-sheet steps for iPhone, two framed screenshots from the screenshot tool.
Generated a QR code pointing at /app?utm_source=qr&utm_medium=table, dark red on cream, restaurant initial in the centre, caption "Scan for the menu — no app store needed". Printed at 35 mm on the table cards and 12 cm on the door. Scanned the print test with two phones before ordering. A second code for the receipts pointed at utm_medium=receipt.
Week 2: the first notification
With installs happening, the owner wanted to announce a new dish. Set up OneSignal, regenerated the service worker with the OneSignal App ID (bumped the cache version), redeployed. Added a pre-prompt to the install page and the menu page for installed users only: "Hear about new dishes and specials — about twice a month?" with a Yes button. iPhone users see it only inside the installed app, as iOS requires. First campaign: one notification, deep-linked to the new dish on the menu page, sent at 11:30 on a Thursday. Opt-outs: a handful. Orders that lunchtime: noticeably up. The owner now understands push as a promise about frequency, and has kept to twice a month.
Month 2: the Play Store question
A few customers asked whether it was "on the Play Store". Because the site was already a valid PWA, listing it as a Trusted Web Activity took an evening: Android option in the builder for the test APK and assetlinks.json (hosted under /.well-known/), Bubblewrap for the AAB from the same manifest, a US$25 developer account, screenshots from the installed app, the checklist for the forms. Closed testing with staff and regulars for the required 14 days, then production. iPhone customers continue to install from the site; nothing changed for them.
What it cost
| Item | Cost |
|---|---|
| Builder, tools, guides | Free |
| Hosting changes | None |
| Printing table cards | Same cards they already reprint seasonally |
| OneSignal | Free tier |
| Google Play account (optional) | US$25 once |
| Time | About an afternoon for the app; an evening for Play |
What would have been different
- On Wix or Squarespace: home-screen install via a hosted manifest, no offline, no push from the site; the Android package route for a Play listing. Perfectly workable, less complete.
- With an online ordering system that needed background location (live driver tracking): still a web app for customers, but the driver side would want native.
- If the owner wanted iOS App Store presence: not achievable with a web app; that would be a native or cross-platform build and a different budget conversation.
Your site is probably closer to this example than to anything in a native-app agency's portfolio. Start with the builder; the rest of the afternoon is described above.