Telehealth · Front-End · SEO
uGlowMD
Telehealth Storefront Rebuilt for Search, Speed and Compliance
React 19Vite 7React Router 7Tailwind v4Static Prerender (SSG)SupabaseStripeVercel


My roleI rebuilt the front end and the delivery layer: routing, prerendering, sitemap generation and the CI performance gate.
The situation
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.
System architecture
- Product data36 medications
- Route configReact Router 7 · 46 routes
- Vite buildReact 19 · prerender (SSG)46/46 static HTML
- Sitemap + robotsgenerated from routes
- Static HTMLreal title per URL
- CI budget gatefails above 200 KB gzships 178 KB
- Crawlers + visitorsreadable without JS
Architecture decisions I made
- 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.
- 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.
- 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.
- 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.
In practice
Measured: live site vs the rebuilt build
- Served HTMLProduct page: 1,972 bytes of empty shell before; 78,012 bytes of real HTML after.
- First paintProduct page FCP: 1,232 ms before, 112 ms on the rebuilt build.
- Server responseHomepage TTFB: 455 ms before, 12 ms on the rebuilt build.
- Page weightProduct page transfer: 2,695 KB before, 561 KB after.
Measured in production
Routes prerendered to static HTML0 → 46
Product page first paint (rebuilt build)−91%
Homepage server response (rebuilt build)−97%
Product page height−84%
Product page weight−79%
Category GIFs re-encoded to H.264−94%
What I took away
- 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.
Gallery









