Skip to content

QR Setu Tech Stack

The canonical record of what QRSETU runs on: the current stack, the target stack it is moving to, why, and what each costs. Kept in sync with the architecture ADRs (esp. ADR-0011).

Guiding principle — zero-burn, free-tier-first

Early-stage budget discipline: the only paid service today is Supabase Pro. Every tech choice below is measured against that — no new paid vendors, everything on open-source frameworks and free tiers, with costs appearing only usage-driven as we scale (never upfront). Framework choices are cost-neutral (all open-source, MIT).

Why we're changing the frontend — the SSR/SEO win

This is the decision that motivates the migration, so it's stated plainly. The current frontend is a client-rendered Vite + React SPA: the browser downloads an HTML shell + a JS bundle, then renders the content after JS executes. That is actively bad for QRSETU's growth engine — the public service cards — in two concrete ways:

  1. Broken social/link previews. Social scrapers (WhatsApp, Facebook, X, Slack) do not execute JavaScript. They read the HTML they receive — which, for a client-rendered SPA, is an empty shell with generic meta tags. So a merchant sharing their QRSETU card on WhatsApp (the primary viral loop in India) gets a blank/generic preview instead of their business name, logo, and description. This is a growth bug hiding in the current stack.
  2. Weaker search indexing + slower first paint. Crawlers can render JS but do it slower and less reliably; the SPA's content isn't in the initial HTML, per-slug meta isn't set at HTTP-response time, and LCP is delayed by the JS round-trip.

The target frontend renders the public cards server-side (SSR/SSG): full HTML + correct per-slug OG/meta + JSON-LD structured data (LocalBusiness/Service) arrive in the HTTP response. Result: rich WhatsApp/social previews, reliable Google indexing (incl. rich results), and fast LCP — behind Cloudflare's cache (>95% hit target). For a product whose growth is shareable, discoverable business cards, this is a step-change, not a nice-to-have.

See ADR-0011 for the full frontend decision (surface-matched: web-DOM for public+admin, universal Expo/RN for the merchant app).

Current → Target mapping

LayerCurrentTargetWhyNew cost
Landing / marketing (/, features, pricing, per-domain SEO pages)Client-rendered Vite/React SPAReact Router v8 (SSR) on CloudflareSEO — the primary traffic driver (organic discovery → signups)$0 (open-source; Cloudflare free tier)
Public service cards (/b/:slug, /s/:slug, /w/:slug)Client-rendered Vite/React SPAReact Router v8 (SSR) on Cloudflare — same React stackSEO + WhatsApp previews + fast LCP (see above)$0
Merchant app (dashboard, editing, analytics)Same web SPAExpo / React Native — Android native (R1), iOS native (R1), responsive web via RNWMobile-first product; true native + structural parity$0 frameworks; builds free (below)
Admin (command center + ops)Web SPA (admin tier)React Router v8 (React + shadcn, desktop-first) + installable PWA for mobile opsDense data UI stays best-in-class; mobile field-demo via PWA$0
UI componentsshadcn/ui (Radix + Tailwind, DOM)shadcn/ui KEPT for public + admin; RN primitives + NativeWind for the merchant appKeep density/a11y where DOM is best; native where the product lives$0
Design tokensTailwind theme / CSS varsOne shared design-token package → compiled to a Tailwind config (DOM) + a NativeWind theme (RN)Single source of truth; zero cross-stack drift$0
StylingTailwind CSSTailwind (DOM) + NativeWind (RN)Same utility/token model across idioms$0
Routingreact-router (SPA)Astro routing + React Router v8 (web) · Expo Router (mobile)File-based, SSR-capable, one tree per stack$0
LanguageJS/JSX + partial TSTypeScript strict (all new code) + JS→TS migration roadmapType safety, shared portable core$0
Data access (client)Mixed supabase.from() + React Query + cacheUtils.jsTanStack Query + RPC/Edge-Function services (retire from() + cacheUtils)The enforced data-access rule; runs identically in DOM + RN$0
Backend / DB / Auth / StorageSupabase (Postgres + RLS + Auth + EF + Storage)Same Supabase + archetype platform (ADR-0009), entitlements (ADR-0007), analytics read model (ADR-0010)No backend re-platforming — it's all UI-agnostic$0 new (existing Supabase Pro)
HostingCloudflare Pages (static)Cloudflare Pages + Workers/Functions (SSR) + Expo web exportSame vendor; adds edge SSR$0 free tier → $5/mo at scale
Mobile builds— (none)EAS Build (free tier) or local / GitHub ActionsCloud or local RN app builds$0 (free tier / local)
Payments— (none)Razorpay (web-first, companion model — ADR-0002)India-first PSP; 0% store commission$0 fixed (~2%/txn)
EmailSupabase Auth built-inZeptoMail (transactional/OTP) + Zoho Campaigns (promotional, later — ADR-0008)Deliverability, branded OTP, India DC$0 to start (free tier → credits)
CI/CDHusky pre-commit; GitHubGitHub Actions pipelines (frontend + backend)Free CI minutes cover early scale$0 (free tier)
Repo structureSingle repo / single appMonorepo packages/{core,tokens} + apps/{web,mobile} (timing TBD)"Extraction is a move, not a rewrite"$0

Cost model (verified 2026-07-20)

ServiceFree allowanceWhen you'd payPaid tier
Frameworks (Astro / React Router / Expo / Next)Unlimited — open-sourceNever
Cloudflare PagesUnlimited static requests/bandwidth; 500 builds/mo
Cloudflare Workers/Functions (SSR)100k requests/day + 10ms CPU/reqSustained SSR beyond free (mitigated by edge caching + Astro static-first)$5/mo (Workers Paid; no daily cap, more CPU)
EAS Build (RN app builds)15 Android + 15 iOS builds/mo (or build locally / GitHub Actions, free)Frequent priority cloud builds$19/mo (Starter)
Google PlayTo publish Androidone-time $25
Apple DeveloperR2 (iOS native) — $0 in R1$99/yr
RazorpayNo fixed feePer transaction~2%/txn
ZeptoMail10k emails on signupHigher send volumecredit-based (cheap)
Supabase(existing — Pro, ~$25/mo)Analytics/storage growthusage add-ons

Net new recurring cost for R1 = $0. The only guaranteed spend is a one-time $25 Google Play registration at publish. No new paid vendor is introduced — Cloudflare is already the host, and every framework is free.

First places cost will ever creep (so there are no surprises)

  1. Cloudflare Workers Paid ($5/mo) — if public SSR volume outgrows 100k req/day. Deferred by the >95% cache-hit design (a cache hit is served from Cloudflare's edge cache and doesn't re-invoke the Worker), so only cache misses run SSR.
  2. Supabase compute/storage add-ons — if analytics rollups/storage get heavy. Deferred by ADR-0010's pre-aggregated rollup design (dashboards read cheap, not raw tables).

Both are usage-driven, small, and arrive only at real scale — consistent with the zero-burn principle.

Frontend architecture at a glance (two stacks, one core)

The 4 tiers map to 2 stacks:

TierAuth?SEO?→ Stack
landing (/, features, pricing, per-domain pages)NoYes — primary traffic driverStack 1 (RR8 SSR, indexed)
public (service cards /b/:slug …)NoYes — viral/share engineStack 1 (RR8 SSR, indexed)
user / merchant (/dashboard/*)YesNoStack 2 (Expo/RN app)
admin (/admin/*)YesNoStack 1 (RR8 web + PWA)
  • Stack 1 — React web (React Router v8, SSR, DOM, shadcn): serves three tiers — landing + public (both indexed, SEO-critical, cached) and admin (behind login, + PWA). Auth pages (/login, /signup) live here too but noindex.
  • Stack 2 — Universal Expo/React Native: the merchant product app only (Android native / iOS native / responsive web via RNW).
  • Shared across both: the platform-agnostic TS core (TanStack Query, Supabase, RPC/EF services, Zod, entitlement/vertical resolvers) and the design-token package (single source of truth for the look).

Programmatic-SEO growth lever: the landing tier can generate per-domain × use-case × city pages (/for/real-estate, /qr-menu-for-cafes, …) from the domain registry (ADR-0009), each SSR'd for high-intent local search — a major organic-growth play, cheap to build on RR8 SSR + the registry. Planned as a post-R1 growth workstream.

Frontend framework — decided (2026-07-20)

React Router v8 (formerly Remix) for both public + admin — Stack 1 is one React framework (ADR-0011 open-Q1, resolved). Chosen for solo-developer maintainability without compromising SEO: RR8 SSR delivers all the SEO fundamentals that drive growth (server HTML, per-slug OG for WhatsApp, JSON-LD, indexing, fast LCP) while keeping Stack 1 to a single React framework (one build, full shadcn reuse). Astro was the max-CWV alternative but would force two web frameworks (Astro can't do the dense admin) — not worth it for one person; its only edge is marginal Core Web Vitals. Escape hatch: the public pages alone can later migrate to Astro if measured CWV becomes a competitive bottleneck — surgical, reversible. Next.js ruled out (OpenNext adapter tax on Cloudflare, no upside here). Cost-neutral.