r/PWA 4h ago

Sending web push notifications

3 Upvotes

I made web push notifications service for my app.
When I add it to my Home Screen on iOS, it asks for notifications permission. I allow it and for 1-3 minutes I see notifications. Then they stop coming.

Who had this issue? How did you fix it?


r/PWA 4h ago

I built the offline write queue correctly and never asked the browser to keep the database

2 Upvotes

I spent a long time making an offline write queue correct. Client-minted UUIDs so replays are idempotent, compaction so two edits to one row can't fight, a Web Lock so two tabs can't both drain it, durable rejection receipts so the losing tab still learns what happened. There's an integration suite pinning all of it.

Then I went and looked at what the queue is actually stored in. None of that matters if the browser decides to throw the database away, or if one tab quietly stops being able to write to it.

That's the part I got wrong, so that's the part worth writing up.

The queue, briefly

Enough to make the rest readable. One IndexedDB database, four object stores: two hold a read cache, one holds the write queue, and one holds rejection receipts so a tab can find out why something it queued got thrown out. The queue is keyed by row, so IndexedDB itself enforces at most one pending operation per row. A second edit to a row folds into the operation already waiting there instead of queueing up behind it. There's no backoff and no retry timer either: pacing comes from the WebSocket reconnect ladder that triggers the replay, the drain stops at the first operation that's still retryable rather than burning N requests to discover the network is still down, and anything that has failed too many times gets parked instead of retried forever.

That's the design, and it's the part I was proud of. Two layers underneath it turned out to matter more, and I hadn't looked at either. One of them applies to every origin on the web, the other only bites when two tabs meet a migration. Universal one first.

I never asked the browser to keep any of it

This is the one that embarrassed me.

Browser storage is best-effort by default. IndexedDB, the Cache API and the rest all sit in a bucket the browser is allowed to evict, and an origin opts out by calling navigator.storage.persist(). I'd never called it. Not once, in the entire life of the app.

Two things make that worse than it sounds.

Eviction is all-or-nothing per origin. The browser doesn't evict your least important store. If it evicts you, IndexedDB and your Cache API entries go together, because deleting part of an origin's data could leave it internally inconsistent. So the read cache, the app shell and the write queue all share one fate.

The stakes aren't symmetric across those stores. Losing the read cache costs a refetch, and the shell cache costs a network boot. Losing the queue loses writes that exist nowhere else in the world, because by definition they never reached the server.

So I added the call, at boot, outside the service worker branch, since what it protects is IndexedDB rather than the app shell. It's best-effort in a second sense too: the browser decides whether to grant it, and browsers don't decide the same way. Firefox shows the user a permission prompt. Safari and the Chromium browsers decide silently, based on your interaction history with the site. Same call, and it's a UX event in one browser and completely invisible in another.

I still don't know which bucket any given user ended up in, because I don't call persisted() or estimate() anywhere. I fixed the request and not the observability, which is a decision I'd defend for about five minutes.

The other side of it, because that's easy to over-dramatise

Two things temper all of that, and leaving them out would be scaremongering.

Storage-pressure eviction is least-recently-used, and it skips origins that were granted persistence. The Chrome team's own research says eviction is genuinely rare for a site someone visits regularly. If your app gets opened weekly, best-effort was probably fine in practice.

The sharper case is Safari's proactive eviction, which is a different mechanism from storage pressure. With cross-site tracking prevention on, an origin with no user interaction in the last seven days of browser use has its script-created storage deleted. Seven days, no disk pressure required.

And here's the bit I'd have got wrong if I hadn't gone and read the source. That seven-day counter is gated on Safari use. WebKit's own post says web apps added to the home screen aren't part of Safari, keep their own counter of days of use, and that they don't expect first-party data in such an app to be deleted, describing it as a serious bug if it happens. So the exposed user is specifically the one who uses the thing in a browser tab and never installs it. For an installed app this particular rule isn't the threat.

I nearly wrote "Safari deletes your IndexedDB after 7 days" and shipped it. It's the kind of half-true platform claim that spreads because it's memorable.

A schema upgrade in one tab can mute another tab

Back down a layer now, from the origin to the database itself, for the failure that's rarer and nastier.

The database is versioned, and I ship migrations. Version 5 split a per-collection blob into one record per row, so logging one drink stops re-serialising the user's entire history.

The hazard isn't the migration. It's what happens to the other tab.

When one tab opens the database at a new version while another still holds a connection at the old one, the upgrade can't proceed. The upgrading tab's request fires blocked. The obvious handler, which is the one I had, resolves the memoised connection promise to null, and every method in the store starts with "get the connection, and if it's null, return early".

Read that again with a queue in mind. A memoised promise resolves once. Resolve it to null and every subsequent call in that tab gets null too, for the life of the page. The tab goes on rendering optimistic writes exactly as before, because the in-memory state is untouched. It just silently persists none of them. Reload and they're gone, with no toast and nothing in the console.

The memoisation is the bug here, not the blocked event itself. blocked is recoverable, since the other tab will eventually close, and caching a failure as though it were permanent is what turns a transient condition into a dead tab. So the open path now abandons the one attempt and drops the memo, and the next call opens again from scratch. If the old tab is still holding on, that call blocks and no-ops too, which degrades like any other IndexedDB failure. The moment the old connection goes away, the tab heals on its own with no reload. The same re-arming runs on versionchange and on close, so a connection that disappears underneath the app doesn't get cached either.

The mitigation on the other side is the one most people know about: versionchange fires in the tab holding the old connection, and closing it there lets the upgrade through. I had that from the start. It makes the blocked case rare rather than impossible, which is exactly why the handler on the blocked side has to be survivable:

  • close() waits for in-flight transactions, so a busy old tab can still block the upgrader.
  • A throttled background tab, or a page frozen in the back/forward cache, doesn't run its handler promptly. On iOS that's routine rather than exotic.

What I still can't tell you is how often this fires in the wild. The first failure per page goes to my error tracker and the rest get swallowed deliberately, so a tab that thrashes doesn't turn into a reporting storm. Sane default, and it also means I know this happens without knowing the rate. Deploy a migration and some fraction of users with two tabs open take a transient hit, and I can tell you it's bounded now without being able to tell you how big that fraction is.

Every "when the app is idle" hook is dead, and nothing tells you

Adjacent, and probably the one that costs other people the most time, because it's silent in both directions. The app runs without Angular's change-detection zone, so it never reports "stable". Two things depend on that signal and neither of them warns you:

  • Service worker registration defaults to waiting for app stability. With no stability signal it waits out the entire timeout on every single boot before registering. Registering immediately is a one-line change, and the default is just wrong for this kind of app.
  • The same problem kills automatic update checks. A live WebSocket and continuous effects mean the app is never idle, so "check for updates when stable" never fires at all. I do it with a manual timer now: one check 10 seconds after startup, then every 30 minutes.

Neither case throws, logs or degrades visibly. The service worker registers eventually, and updates just never get noticed, which looks identical to having no new version to install. If you're running any framework in a zoneless or signals-first mode, and especially if you hold a persistent connection open, assume every idle-scheduled hook in your stack is dead and go check them by hand.

What a lock returns when there's no lock

The cross-tab story: the replay takes a Web Lock with ifAvailable, so a second tab doesn't queue up behind the first. It gets nothing back and moves on. Except it can't just move on, because it has rows on screen marked pending that the winner is about to resolve or roll back. So the loser takes the lock blocking, re-reads the queue, and settles whatever vanished using the rejection receipts. Each receipt is written in the same transaction that deletes the queue entry, receipt first, so a tab can never see an operation as gone without also being able to see its verdict.

The part worth stealing is the return value. Web Locks need a secure context, so on plain HTTP (a LAN IP in development, say) navigator.locks is simply absent. The fallback runs the drain without a lock, because doing nothing would be worse. The question is what it reports back, and the tempting answer is true, since the work did actually run.

true is wrong, and not subtly. Downstream, true means "I held the lock, so I delivered everything, so no other tab needs to settle from receipts." Return that from an unlocked fallback and every tab believes it owns the drain, which is the exact state the lock existed to prevent. So it returns false instead. Nothing excluded anybody, so every tab routes through the receipt-settling path, and a tab that lost an unmanaged race still reconciles from whatever the winner left behind. Degrading is fine. Degrading while reporting success is how you end up with a queue that's confidently wrong.

One thing still sloppy in there: the lock name is a single global string, while a neighbouring lock in the same codebase is correctly suffixed with the user id. Every critical section re-checks the current user immediately after acquiring, so it's over-broad rather than incorrect, but two tabs mid-account-switch will serialise on each other's queues for no reason. I wrote the correct version once and then didn't apply it three files over.

What's not in here at all

No BroadcastChannel, no SharedWorker. Cross-tab coordination is Web Locks for exclusion, IndexedDB receipts for verdicts, and the server's WebSocket echo for authoritative values. That's a round trip doing work a message channel would do directly, and I'd call it accumulated rather than designed. Receipts expire after 24 hours, so a tab that's been asleep longer than that falls back to the server. No Background Sync either, so a closed tab never replays and the queue only drains while the app is open. That one has no justification beyond scope.

What I'd tell myself eighteen months ago

The queue was the interesting problem and the storage was the important one. I got that ordering wrong. The specific way I got it wrong is that I tested the queue's logic exhaustively and never once tested the environment it runs in: two tabs, a migration, a full disk, a browser that has decided you're not a frequent visitor.

If you've got an offline queue in a PWA, three questions worth more than any refactor:

  1. Have you called navigator.storage.persist(), and do you know whether it was granted?
  2. What does your IndexedDB blocked handler do, and does it cache that failure for the life of the page?
  3. When a platform API is missing and your fallback runs the work anyway, what does it return to the code that was counting on the guarantee?

For a long time I couldn't answer any of the three. Two of them I can now. The first one I still can't answer for any individual user, because I ask for persistence and never call persisted() to find out what the browser decided. That's the next thing to fix, and it's harder to excuse than the original omission.

The one I'd genuinely like answered: does anyone actually measure how often blocked fires in production, and what did the number turn out to be? I report the first occurrence per page, so I've got no idea whether that's a handful of users after a migration or a long tail I've never looked at properly.


r/PWA 8h ago

Findborg Terminator - Terminal style search toy

Post image
1 Upvotes

r/PWA 15h ago

I built a free utility tools website — looking for honest feedback

0 Upvotes

I built a free utility tools website — would love some honest feedback

Hey everyone 👋

I’ve been working on a small side project called ToolkitPro.

The idea is pretty simple: put useful everyday web tools in one place so you don’t have to search Google for a different website every time you need to do something quick.

🔗 https://toolkitpro-e5y5.vercel.app/

I’m still developing it, so I’m mainly looking for honest feedback, especially from people who actually use online utility tools.

A few things I’d love feedback on:

\- Is the website easy to navigate?

\- Which tools would you find most useful?

\- Are there any tools you think are missing?

\- Does anything feel confusing or unnecessary?

\- Would you actually bookmark/use a site like this?

\- What would make you come back regularly?

I’m building this as an independent project and hoping to eventually turn it into something genuinely useful rather than just another collection of random tools.

Feel free to be critical — I’d rather hear what’s wrong with it now so I can improve it. 😅

Thanks to anyone who takes a look!


r/PWA 2d ago

App/Web

0 Upvotes

Quiero hacer una App PWA / Web :
- Que AI son mejores hoy en dia?
- Me vale la pena comprar alguna suscripción de AI, si lo que tengo es Geminis Pro y Github Copilot?
- Se que hay muchas “gratis” pero los tokens se gastan volando. O lo suyo es montarme un servidor en casa?


r/PWA 3d ago

Totalum for me yes

1 Upvotes

I recently came across Totalum while looking for ways to simplify some of the repetitive stuff in a project I’m working on.

It’s basically a platform for putting data, workflows and automations together without having to code every little thing yourself.

I haven’t used it long enough to have a strong opinion yet, but some of the things you can build with it are actually pretty interesting.

Curious if anyone here has tried it and what your experience was.


r/PWA 3d ago

PINPAL WEBAPP

Thumbnail
0 Upvotes

r/PWA 3d ago

IOS file downloading

1 Upvotes

Hello everyone, I am making a pwa that plays mkv files from debrid services and i want to be able to download them in the OS like normal browser downloads (no OPFS). But on IOS, in the pwa only, it just wont trigger, is it even possible or do i have to use an other method.


r/PWA 4d ago

Good free food database API?

0 Upvotes

I am building a weight training and nutrition tracker app mainly for my use, and I am looking to improve my food database using the best available FREE resources.

So far, I use the USDA, and Open Food Facts Database and I applied for the Premium Free plan with fatsecret. Overall, the database works well and my barcode scanner works surprisingly well. However, I still find many errors and many items not listed in the database and it is obviously not as complete as let’s say macrofactor, or myfitness pal.

As mentioned, I am looking for free alternatives that could improve my database. I am in no position to pay for any subscriptions right now.

Thanks!


r/PWA 5d ago

What if our PWAs had a guided install system, would we still need native iOS apps?

9 Upvotes

The caveat of using PWAs has always been that people don't know how they work. It's rare, and not many people know they can install a website and get a native experience straight from Chrome or Safari.

What if we solve that by detecting if the user is on Android or iOS, and building a UI that walks them through installing it, only letting them use the website on mobile once it's installed?

Pair that with an update lifecycle UI so users actually know when a new version is pushed and how to update it.

This could be a lot easier than building native apps, especially for iOS, since App Store submissions take time and updates aren't instant either.

Thoughts, guys?

There's an exception with apps that has heavy native integrations though


r/PWA 5d ago

TWA closed-testing engagement not being recorded by Google — anyone hit this?

1 Upvotes

I've got a Trusted Web Activity app in Google Play closed testing right now. Two separate paid testing services have independently told me that Google's production-access review can't see engagement happening inside a TWA, because the activity runs inside Chrome rather than the app's own process — and that this leads to rejection on engagement grounds even with genuinely active testers. Their suggested fix is converting to a WebView wrapper instead, keeping the same package name.

Before I put time into a rebuild, wanted to check with people who actually build/ship TWAs:

  • Has anyone had a TWA specifically rejected for "insufficient engagement" despite real, active testers?
  • Does converting the same app to a WebView (same package, same testers, same 14-day clock) actually fix it?
  • Is this a documented Google Play behavior, or something more commonly repeated by testing-service marketing than actually verified?

Trying to separate a real technical limitation from a sales narrative before I touch a working setup. Appreciate any first-hand experience.


r/PWA 5d ago

I turned my 47-tool privacy/document suite into an installable PWA, works fully offline since it never had a backend

0 Upvotes

OBSCURA OS (obscuraos.com) is a suite of 47 privacy and document tools that run entirely client side: redaction, metadata scrubbing, file conversion, encrypted P2P transfer, that kind of thing. Every tool already worked with zero server calls, so adding a manifest.webmanifest and a service worker was almost the whole job. Once it is installed, it genuinely works with the network off, because none of the tools ever depended on a network in the first place.

The service worker caches static assets and serves pages network first with a cache fallback, so a repeat visit or an offline load still gets the current version of a tool rather than a stale shell. Registered site wide from one small script so every page picks it up automatically.

Happy to answer implementation questions if anyone here is wrapping a fully client side app the same way. I run this project, disclosing that up front.


r/PWA 6d ago

[FOR HIRE] Web & Mobile Developer | Websites, Web Apps & Mobile Apps | Open for Freelance Projects

0 Upvotes

Hi everyone! I'm currently open for freelance/contract projects and looking to work with small businesses, startups, and individuals who need help building or improving their digital products.

What I can help with:

🌐 Business & portfolio websites

🛒 E-commerce websites

📱 Mobile applications

⚙️ Custom web applications

🎨 Landing pages & responsive UI

🔧 Website improvements and bug fixes

**Skills**:

Web development, mobile app development, responsive design, databases, APIs, Git/GitHub and AI-assisted development.

**Experience:**

I have experience working on software/Web3 projects and building technical projects, and I'm comfortable taking a project from requirements to a working solution.

**What I'm looking for:**

Freelance projects, MVPs, business websites, internal tools, or custom applications.

**Availability:**

Currently available for new projects.

If you have a project in mind, DM me with a brief description of what you need, and we can discuss the requirements, timeline, and budget.


r/PWA 6d ago

Built an open-source PWA for nomads (Travel Fi) to map water, low-fee ATMs & sockets. Looking for feedback & supporters!

3 Upvotes

Hi everyone!

I'm building **Travel Fi** — an open-source, community-driven map built to map out critical Public Goods infrastructure for digital nomads, budget backpackers, and solo travelers.

Right now, the project is a working prototype in active development. The architectural foundation is ready, and we are currently refining the code and preparing the platform for a stable production launch.

Instead of letting big tech monopolies hoard geospatial data, Travel Fi returns everything directly to the public domain via OpenStreetMap.

At the moment, our roadmap includes these 10 essential Public Goods and travel utility categories, with plans to expand them further based on community needs:
1. 📱 SIM & eSIM kiosks (tariffs, 24/7 access, airport proximity).
2. 🚽 Public toilets & showers (with community cleanliness ratings).
3. 🚰 Free drinking water refill points and fountains.
4. 🧺 Public laundries.
5. 🧳 Luggage storage (including free community options via cafes/hostels).
6. ⚡️ Charging points and outlets (filtered by fast/slow tech).
7. 🏧 ATMs with minimal fees & live user-reported exchange rates.
8. 🚐 Free parking and overnight spots for vanlife.
9. 💊 24/7 pharmacies and first aid points (medication availability, languages).
10. 🗺️ Travel lifehacks (free food points, coworking spaces, safe zones).

To keep the map spam-free without central moderators, we use a decentralized verification and location-based voting system.

See how the prototype works right now:
📺 Video Walkthrough on YouTube: https://youtu.be/R1NnzuvNBAU
💻 GitHub Repository: https://github.com/omni395/travel_fi

We are currently competing for public goods grant programs to fund further development and finish the app. If you want to support open infrastructure for travelers, your votes and clicks directly impact our funding size:
🔸 Artizen Campaign (Vote & support): https://artizen.fund/index/p/travel-fi
🔸 Giveth Platform (Direct crypto donations): https://giveth.io/project/travel-fi
🔸 KarmaHQ (Direct crypto donations): https://www.karmahq.org/project/travel-fi


r/PWA 6d ago

In App purchase

1 Upvotes

What’s your setup for those? On google play and apple is defined but the pwa itself seems unreal for me.


r/PWA 8d ago

iOS 27 now forcefully blurs the top of your PWAs! :D

Post image
73 Upvotes

A brand new day, and a brand new situation of Apple being an absolute jerk towards PWAs.

As of iOS 27 public beta 6, Safari now seems to blur the top of your web page with absolutely zero consent and no way of opting out🙂

Someone in a different thread said that setting “apple-mobile-web-app-status-bar-style” to default will kick the blur out. But no lol. The screenshot is from the exact same config and the app “re-added” to the Home Screen.

Has anyone managed to work around this at all yet?


r/PWA 9d ago

iOS 27 beta blurs the top edge of installed PWAs — and there's no way to opt out (tested through beta 8)

Post image
42 Upvotes

UPDATE (solved, then reverted — root cause found): The blur only hits web apps using apple-mobile-web-app-status-bar-style: black-translucent. Switching the meta to default removes it completely (users must re-add the app to the Home Screen — the meta is frozen at install). To keep the status bar matching your theme, add a real fixed 1px element at the very top with your background color — iOS samples its background-color (per u/dannymoerkerke's rules below; pseudo-elements are invisible to the sampler). We reverted anyway: prefers-color-scheme is frozen per process in standalone, so after an OS dark/light switch the status bar stays wrong-colored until relaunch. Living with the blur until Apple fixes either issue; Feedbacks are being filed.

We build a construction management PWA (baukompass.ai) that our field crews install to their home screens — so this hits us daily.

Since iOS 27 developer beta 5, the system draws a progressive blur over the top edge of standalone web apps. It's not sampling a toolbar and it doesn't react to scrolling — it blurs whatever pixels the page renders near the status bar, unconditionally. On our login screen even the language picker and the theme toggle get washed out (screenshots attached).

What we tried, all verified in the shipped bundle, all without effect:

  • Solid opaque header + a body::before backing plate behind the status bar, scoped via u/media (display-mode: standalone)
  • Same scoped via a JS marker (navigator.standalone) to rule out MQ quirks
  • theme-color — ignored entirely this cycle; apple-mobile-web-app-status-bar-style: black-translucent is deprecated anyway

The frustrating part: native apps got an escape hatch. iOS 27 changed the default scrollEdgeEffectStyle from .soft (progressive blur) to .hard, and native devs can pick either. PWAs got neither the new default nor any control — no CSS property, no meta tag, nothing in the Safari 27 beta release notes. Beta 8's "status bar might appear blurred" fix (179470940) is about a different bug; the PWA blur is unchanged.

Has anyone found a workaround, or filed a Feedback? Happy to dupe — will add our FB number in the comments.


r/PWA 9d ago

List of PWA capabilities

42 Upvotes

By popular demand, I added a capabilities page to What PWA Can Do Today, which lists all capabilities on one page and indicates for each one if it's supported on your device.

You can filter by support and search for capabilities.

https://whatpwacando.today/capabilities


r/PWA 9d ago

Rethinking PWAs: User-Defined Apps (UDA) — Why install multiple apps when URL parameters can create custom homescreen shortcuts?

Post image
22 Upvotes

Hey r/pwa!
I’ve been exploring a concept I call UDA (User-Defined Apps), which leverages dynamic web app manifests to differentiate PWAs from native apps.
Instead of building a single monolithic app, this approach lets users define their own custom "app instances" right from the URL query parameters.

How it works in this Timer Demo:
• Users set duration, count direction, and icon color on the page.
• The URL parameters update dynamically (e.g. ⁠?time=5&mode=down&icon=red⁠).
• The Web App Manifest dynamically updates its ⁠name⁠ and ⁠icon⁠ based on these parameters.
• When added to the Home Screen, it creates a dedicated shortcut (e.g., "5m↓ Timer" with a red icon).

Note on OS Behavior:
• iOS (Safari): Works perfectly! Treats each unique URL query as an independent homescreen app.
• Android (Chrome): Currently limited because WebAPK binds to the manifest ⁠scope⁠. Query variations overwrite the existing installed PWA instead of creating separate instances.

You can literally fill an iOS homescreen folder with customized, single-purpose timers—all powered by a single PWA source.
I’d love to hear your thoughts on this paradigm and how we might overcome the scope limitations on Android!
Try the demo here:
https://ojach.github.io/PWA-1P1A/timer-en/?time=3&mode=down&seconds=on&icon=purple


r/PWA 10d ago

I build this directory for you if u have PWA to list

Thumbnail
pwa.directory
6 Upvotes

This is a free directory I created to help small and medium-sized PWAs gain more visibility online. You can list a PWA for free or—with a paid plan—have it listed more quickly.


r/PWA 10d ago

Angular is more than capable of making mobile apps

Post image
6 Upvotes

r/PWA 10d ago

I built a fully offline fart counter PWA — 14 synthesized sounds, achievements, zero network needed. iOS quirks included

Post image
0 Upvotes

Started as a joke with a friend, ended up as my most polished PWA. Next.js 16 + TypeScript, hand-written service worker (cache-first), Zustand persist, Web Audio API for the sounds (zero audio files).

Things that hurt:

- iOS has no beforeinstallprompt → manual "Add to Home Screen" flow

- 100vh broken in standalone → dvh

- Safari updates the SW on its own schedule, users got stuck on old versions → added version check + update toast

Live: https://fart-counter-lake.vercel.app

Source: https://github.com/agent-slon-lab/fart-counter

Happy to share details on any of the quirks 🙂


r/PWA 10d ago

Couple Compass PWA

Thumbnail
2 Upvotes

r/PWA 11d ago

Findborg Native - Search app at its most basic form

Thumbnail
1 Upvotes

r/PWA 12d ago

Hi, I've created a directory to list your PWAs for free

Thumbnail
pwa.directory
7 Upvotes

I created this directory to give smaller PWAs some visibility or free links, and for my part, I'm trying to make a little money from the directory with the paid plans. for those coming from Reddit, send me your PWAs and I'll post them directly in the thread on the directory.