Qalab Hassnain AghaCo-Founder & CTO

Telehealth · Front-End · SEO

uGlowMD

Telehealth Storefront Rebuilt for Search, Speed and Compliance

React 19Vite 7React Router 7Tailwind v4Static Prerender (SSG)SupabaseStripeVercel
uGlowMD: Homepage · rebuilt, desktop
uGlowMD: Homepage · mobile, mobile

My roleI rebuilt the front end and the delivery layer: routing, prerendering, sitemap generation and the CI performance gate.

Open view-source on any page of the live store and you get the same thing: a 1,972-byte empty shell with 35 characters of text and one title shared by all 46 URLs. Crawlers that don't run JavaScript saw nothing.

Product pages had the opposite problem for humans: 9,439 px of compliance text between the buyer and the dosage they came for.

  • Telehealth: per-medication safety information must stay complete and reachable.
  • Keep the existing React SPA stack (React 19, Vite 7) rather than a platform migration.
  • Make performance a property of the build, not a one-off cleanup.
prerenderbundle sizeProduct data36 medicationsRoute configReact Router 7 · 46 routesVite buildReact 19 · prerender (SSG)46/46 static HTMLSitemap + robotsgenerated from routesStatic HTMLreal title per URLCI budget gatefails above 200 KB gzships 178 KBCrawlers + visitorsreadable without JS
  1. Product data36 medications
  2. Route configReact Router 7 · 46 routes
  3. Vite buildReact 19 · prerender (SSG)46/46 static HTML
  4. Sitemap + robotsgenerated from routes
  5. Static HTMLreal title per URL
  6. CI budget gatefails above 200 KB gzships 178 KB
  7. Crawlers + visitorsreadable without JS
Prerendered SPA with an enforced performance budget
  1. D1Prerender every route from the router's own dataAll 46 routes are rendered to real HTML at build time, with a real title per URL. Crawlers and slow phones get content without executing JavaScript.
  2. D2Sitemap generated from the same source as the routesThe sitemap is built from the data the router renders and filtered through robots.txt, so it cannot drift out of sync with the site.
  3. D3Safety information gets its own routeMoving per-medication Important Safety Information to a dedicated page kept it complete while product pages dropped from 9,439 px to 1,547 px.
  4. D4A JavaScript budget that fails the buildCI fails if landing-route JavaScript passes 200 KB gzipped. It ships at 178 KB, and it stays there because regressions cannot merge.Trade-off Every new dependency has to earn its bytes.

Measured: live site vs the rebuilt build

  1. Served HTMLProduct page: 1,972 bytes of empty shell before; 78,012 bytes of real HTML after.
  2. First paintProduct page FCP: 1,232 ms before, 112 ms on the rebuilt build.
  3. Server responseHomepage TTFB: 455 ms before, 12 ms on the rebuilt build.
  4. Page weightProduct page transfer: 2,695 KB before, 561 KB after.
Routes prerendered to static HTML0 → 46
Before0
After46
Product page first paint (rebuilt build)−91%
Before1,232 ms
After112 ms
Homepage server response (rebuilt build)−97%
Before455 ms
After12 ms
Product page height−84%
Before9,439 px
After1,547 px
Product page weight−79%
Before2,695 KB
After561 KB
Category GIFs re-encoded to H.264−94%
Before34 MB
After1.9 MB
  • An SPA is invisible to any crawler that does not execute JavaScript. Prerender or accept it.
  • Performance budgets only hold if the build enforces them.
uGlowMD: Semaglutide product page · dosage dial, desktop screenshot
Semaglutide product page · dosage dial
uGlowMD: Weight loss category, desktop screenshot
Weight loss category
uGlowMD: Testosterone category, desktop screenshot
Testosterone category
uGlowMD: Safety information · its own route, desktop screenshot
Safety information · its own route
uGlowMD: Knowledge base, desktop screenshot
Knowledge base
uGlowMD: Before · original homepage, desktop screenshot
Before · original homepage
uGlowMD: Before · original product page, desktop screenshot
Before · original product page
uGlowMD: Semaglutide · mobile, mobile screenshot
Semaglutide · mobile
uGlowMD: Tirzepatide · mobile, mobile screenshot
Tirzepatide · mobile
uGlowMD: Weight loss · mobile, mobile screenshot
Weight loss · mobile