Challenge
A Series-A B2B SaaS platform had outgrown its launch-day site. Three years of feature announcements had layered on top of each other without a coherent information architecture. The result: 32 pages of navigation, 3.4 MB of JavaScript on the homepage, and a value proposition buried three clicks deep.
Marketing was spending on paid to compensate. Every new channel they tried would convert worse than expected. The product was good; the site was the problem.
Diagnosis
Two signals told us the story before we wrote a line of code:
- Session recordings: visitors landed, scrolled once, left. Almost no one clicked the primary CTA.
- Search Console: the company name ranked #1. Everything else was stuck on page 3–5.
Combined with a Lighthouse score in the 40s, it was clear: the site was hostile to both search engines and buyers.
Approach
Re-think the information architecture
We collapsed 32 pages to 14 by grouping content around buyer intent rather than feature teams. The new primary nav had four items. The most important one was "how it works," a page that didn't exist before.
Rebuild with performance budgets
Next.js 16, React Server Components by default, a hard 150 KB JS ceiling per route enforced in CI. Images moved to next/image with explicit dimensions. Fonts via next/font for zero CLS. Tailwind v4 for styling — no utility-first bloat, no CSS-in-JS runtime.
Wire analytics before launch
GA4, Search Console, conversion events for every CTA variant, rank tracking for the 40 keywords that mattered. When launch day came, we had a baseline inside 24 hours, not six weeks later.
Instrument the content engine
MDX in the repo, Zod-validated frontmatter, automatic schema, automatic sitemap. The content team went from "file a ticket with engineering" to "open a PR with the post," which 10× their publish cadence.
Stack
Next.js 16 · React 19 · TypeScript strict · Tailwind CSS 4 · shadcn/ui · MDX · Zod · GA4 · Search Console · Docker · nginx · Let's Encrypt
Outcome
Three months in, the numbers moved on every dimension we tracked:
- Bounce rate down 58% — people were finally finding what they came for.
- Demo requests 2.1× higher — same traffic, different conversion.
- LCP at 0.9s — down from 3.8s; Lighthouse in the 90s across templates.
- JS shipped per route: 112 KB — down from 3.4 MB on the homepage alone.
- Content team publish cadence 10× — from roughly one post a quarter to one a week.
What we'd do differently
We should have instrumented the old site before the rebuild, not just the new one. Comparing new-site metrics to Google Analytics Universal exports was messier than it needed to be. Next time we'll capture a four-week baseline on the old site before a single new line of code ships.
Pull quote
> “The site used to be something we defended in every meeting. Now it's something we send people to.”