Personal product, 2026
Everything: product, mobile, web, API, database, auth, payments, analytics, admin, deployment and SEO
VM Academy
My own learning platform, built alone across mobile, web, API, database, payments, analytics and deployment.
In development
A place to learn several skills in one sitting rather than one skill across five abandoned tabs. Five domains, twenty-one categories, tracks that run across them, and an invited faculty rather than a marketplace with two hundred thousand listings and no editor. I designed the product and built every part of it.
It is three separate repositories with nothing importing across the boundary: an Expo mobile app, a TanStack Start web app and a Hono API over Postgres. The web app alone carries three audiences, the marketing site, the signed-in student experience and an admin panel, which is what decides most of the routing and the authorisation shape. Instructors get their own navigation group on mobile for authoring and submitting courses.
Money is the other axis. It sells a membership and a one-time purchase at the same time, through Razorpay, with instructor applications, review and payouts in the first version rather than deferred. Web sells and mobile learns, which is a deliberate split that keeps store billing out of the purchase path.
Surfaces
- mobileReact Native and Expo
Students browse, learn and track progress; instructors author and submit. React Navigation with stack, tab and nested navigators, and universal links wired through to both platforms.
- React Native
- Expo
- React Navigation
- TanStack Query
- Zustand
- Better Auth
- Razorpay
- Firebase
- PostHog
- webTanStack Start on Nitro
Marketing site, student experience and /admin in one application. Mux for video playback and direct uploads, and the SEO surface for the whole product.
- TanStack Start
- TanStack Router
- TanStack Query
- Nitro
- Mux
- Tailwind
- Better Auth
- Cloudflare
- serverHono on Node
REST over Hono, Drizzle against Postgres, Razorpay for payments, Resend for mail, Firebase Admin for push. Each client carries a generated contract file, so no client imports server code.
- Hono
- Drizzle ORM
- Postgres
- Better Auth
- Razorpay
- Resend
- Firebase Admin
- Zod
- Render
What it does
- A catalog across five domains and twenty-one categories, searchable and filtered by level and price
- Tracks that run across categories, with their own progress and optional steps
- A custom lesson player with notes, resources, Q&A, up-next and autoplay
- Free courses, individually purchased courses, and courses unlocked by a membership
- Membership and one-time purchase sold side by side through Razorpay
- Progress tracking, XP, daily streaks and cross-domain skill badges
- Live workshops with seat counts, booking, and the joining link on the card once booked
- Sign-in by emailed one-time code, Google or Apple, then onboarding picks domains and goals
- An instructor role with its own navigator: own courses, review notes, submit, and a critique queue
- Admin panel for courses, content review, users, domains, skills and platform metadata
- Light and dark theming that follows the system, and haptic feedback throughout
- Deep links, iOS universal links and verified Android app links into course content
Engineering notes
- Three repositories with no cross-imports; each client holds a generated contract of every response type it uses, regenerated from the server rather than hand-edited
- One Better Auth configuration, mounted by both the API and the web application, with the admin plugin driving role checks
- Publishing is enforced in middleware, not by hiding a button: an instructor can submit, only an admin can publish, and the API refuses a non-draft status from a non-admin
- Entitlement resolved server-side from three independent sources: a free course, an owned enrollment, or an active membership that includes the course
- Two video providers behind one set of controls: Mux through expo-video with signed playback and optional Widevine and FairPlay, and YouTube through a webview whose iframe is oversized and pulled up so none of its chrome survives
- No styling library anywhere — plain StyleSheet over one token file shared verbatim with the web app, so the New Architecture style props replace several dependencies
- Three kinds of state split on purpose: server state in TanStack Query and never copied into a store, device state in Zustand over MMKV, and session and theme in contexts because they gate the navigator
- The auth store is AES-256 encrypted with its key in the platform keystore; ordinary preferences are not, because encrypting everything only hides which part mattered
- A failed read renders a retry, never an empty state — "nothing here" is a claim about the student’s account, not about the request
- Nothing routes on the session until it has resolved, so a returning student never sees the Welcome screen flash
- Push split deliberately: Firebase Messaging owns the token and delivery, expo-notifications owns permissions and foreground presentation, and muting releases the token rather than dropping messages on arrival
- Nothing calls an analytics SDK directly — one module fans a typed event union out to PostHog, Firebase and Crashlytics, narrowing props for the one that drops an event on a null
- Sentry removed rather than kept alongside Crashlytics: it was 18.7% of the JavaScript bundle, and dropping it took the Hermes bytecode from 7.4MB to 5.9MB
- The purchase button is Android-only, because App Review 3.1.1 rejects an iOS app that routes digital-content payment outside in-app purchase
- The device is never the authority on a payment: the app polls its own status route until the Razorpay webhook has settled the order
- Nitro Fetch on the native side for the request path, Zustand for client state and TanStack Query for everything the server owns
- Deployed by me end to end, Cloudflare in front of the web app and Render behind the API, on a custom domain with the SEO surface built rather than bolted on
Walkthrough
Seventy seconds on a real phone: home and its rails, the catalog, filtering by domain, a course page, Learn, Profile, and the system theme flipping with the app following it live.









