Example: One Codebase as a Telegram Mini App and a PWA
Some customers live in Telegram. Some will never open a bot. Subeasy-style dual door: local-first UI, Telegram initData on one path, a normal session on the other, Stars when you are inside the messenger. How I would ship it without forking the product.
A Mini App that can only open inside Telegram is a product with one door. That is correct for a salon whose clients already write in the bot. It is wrong for a tracker, a course cabinet, or a small SaaS whose next user arrives from an Instagram bio. Subeasy - a subscription tracker that is both a PWA and a Mini App - is the pattern I would steal for that second case.
The job in this example is a personal subscription list: Netflix, gym, Adobe, the domain you forgot. The interesting part is not the list. It is that the same screens work in a Telegram sheet and on a home-screen icon, without maintaining two apps.
1. The two doors, one product
Detect the host. Do not detect a marketing story.
- Inside Telegram: `initData` is present. Validate HMAC on the server, session = Telegram user id. Theme from `themeParams`. MainButton for “add” and “upgrade.” The bot can remind “Adobe charges in 3 days.”
- In a browser / installed PWA: no `initData`. Magic link or passkey. Same list UI. Bottom CSS button instead of MainButton. Notifications via Web Push where it works; an in-app bell on iOS.
- Local-first either way. IndexedDB or a tiny local store holds the list. Sync to Postgres when a session exists. Subeasy’s point: the first five subscriptions get added before anyone meets a login wall.
2. What must stay shared, what must fork
If you fork screens, you will ship a Mini App that lags the PWA by three weeks. Fork adapters. Keep pages.
- Shared: domain model, list UI, charts, export, pricing copy. One Next.js app. A `runtime` object: `platform: "telegram" | "web"`.
- Fork: auth adapter, chrome (MainButton vs CSS), haptics, CloudStorage vs localStorage, pay. Telegram path can open Stars. Web path opens Stripe or Mono. Same entitlement row in Postgres.
- Bind accounts later, not on first paint. If a user starts in the PWA then opens Telegram, offer “link this Telegram” once. Do not force email inside the Mini App on minute one.
3. Monetisation without painting two checkouts
Premium is a flag on the user, not a SKU per platform. The wall is the same: themes, export, family seats, AI “you are paying 3× the median for streaming.” How money enters changes with the door.
- Telegram door: Stars for a month of premium. No Apple cut beyond what Telegram already takes. Good for 4-12 € equivalent. The bot can send the receipt.
- Web door: Stripe or Mono subscription. Same webhook writes `premium_until`. Do not invent a second plan name.
- Free tier stays useful. Subeasy keeps the tracker unlimited on purpose. If the free Mini App is a demo, people will not install the PWA either.
4. When I would not bother with a PWA
If 90% of the work happens in a group that already exists, a second door is vanity. The expense-split example does not need a PWA. A food-delivery menu for regulars who only order in Telegram does not either.
- Build dual door when strangers arrive from a URL: Instagram, a website, an email. The Mini App is for the cohort that already chats with you.
- Do not wrap a desktop admin as a PWA “because we can.” The sheet is still phone-first. Admin stays a normal website.
Same screens, two hosts, one ledger
I would open with this pattern for a tracker, a course cabinet, a small CRM for freelancers, or any tool that must survive both “I live in Telegram” and “I will not install a bot.” Next.js, a `runtime` switch, local-first list, Postgres when they log in, Stars inside Telegram and a boring card checkout on the web. If you want that dual door instead of two codebases - write via the form. We will ship the Telegram path first if that is where the money already sits, then hang the PWA on the same routes.
Need this built, not just explained?
Telegram Mini App development: Next.js, payments, bots. Direct contractor.
Ready to discuss your project?
I'm a senior web engineer specializing in React and Next.js - available for freelance projects worldwide.
Location
Kyiv, Ukraine
Upwork
View ProfileTelegram
Contact meViber
Contact me