I Made My Knowledge Base 2x Faster: Here's Exactly What Was Killing It
A straight-talk walkthrough of the latency and performance fixes on this site: RSC payload bloat, a 15MB home bundle, fonts, Vanta on every page, and incremental image optimization, with real before/after Lighthouse numbers.
Okay, gather 'round
Alright. Pull up a chair.
When I started the research and PoC for HostKid, I wasn't thinking "performance project." I was thinking hosting company in a lab: CloudStack, networks, volumes of notes, diagrams, the messy real decisions. This knowledge base was supposed to hold all of that so future-me (and anyone following along) could actually read it.
And for a while it felt fine… until HostKid grew. Longer pages. More images. More Mermaid. More chapters linked from the sidebar.
That's when it stopped feeling fine.
I started noticing it in the wild: pages that hesitated, tabs that spun a beat too long, and then the dashboards started talking back. Cloudflare insights, traffic graphs, the little "something's heavy here" signals you ignore once and then can't unsee. So I ran Lighthouse on the HostKid overview like a guilty conscience.
And Lighthouse looked at me like:
Your "documentation site" is downloading fifty megabytes for one page and taking thirty-six seconds to become interactive.
Yeah. That was a vibe.
So today I'm not going to dump theory on you. I'm going to show you what was broken, how we fixed it, and the before/after numbers, in plain English.
Spoiler: average Performance went from ~47 to ~99. HostKid Time to Interactive went from ~36s to ~1s.The "oh no" moment
I wired up Lighthouse CI on this repo so every push gets soft performance budgets. First real scan on the heavy HostKid overview page:
| Metric | First scan (HostKid) |
|---|---|
| Performance | 42 |
| Time to Interactive | ~36 seconds |
| Total page weight | ~49 MB |
RSC index.txt downloads | ~22 x ~2.3 MB each |
Twenty-two files. Two megabytes each. On a docs page.
Docs pages were downloading many ~2.3 MB index.txt RSC payloads. Not one. Many. Every navigation felt like opening a zip bomb.
That's not "needs a CDN." That's "we're shipping the entire library inside every chapter."
Fix #1: Stop stuffing every page with every document body
Here's the simple version.
Next.js App Router sends RSC payloads (index.txt) so the client can hydrate and navigate. We were passing full Contentlayer docs, including body.raw, into client components that lived in the layout:
- Search got every MDX body on every page
- Sidebar got every MDX body on every page
So prefetching a few related docs meant: download the whole corpus again.
What we did instead
- Sidebar only gets metadata: title, url, slug, order (
NavDoc) - Search builds
public/search-index.jsonat build time and lazy-loads it when you open search - Layout stops dragging Contentlayer bodies into the client tree
Same sidebar. Same search. Zero multi-megabyte RSC spam.
After that fix alone, HostKid overview went roughly:
| Metric | Before | After RSC fix |
|---|---|---|
| Performance | 42 | 72 |
| TTI | ~36s | ~3.5s |
| Page weight | ~49 MB | ~2.4 MB |
| RSC transfer | ~45 MB | ~0.4 MB |
Fix #2: The home page was a secret 15MB JavaScript brick
This one made me laugh and cry at the same time.
The home page was a giant 'use client' component that imported allDocs, allBlogs, allCertifications from Contentlayer.
Translation for humans:
"Show the latest 3 cards" accidentally compiled into "ship every document body into the home JavaScript chunk."
Uncompressed home chunk: ~15 MB.
And Next was happily prefetching / from the navbar onto docs pages. So docs paid for home's mistakes.
Fix
- Server page prepares slim card props only
- Client home gets those props, not the whole corpus
prefetch={false}on Home links
| Metric | Before | After |
|---|---|---|
| Home route First Load JS | ~2 MB class disaster | ~126 KB |
Home page-*.js chunk | ~15 MB | ~37 KB |
That's not a tweak. That's deleting a plot twist from the bundle.
Fix #3: Three.js / Vanta was crashing the party uninvited
I love the dark-mode Vanta NET background on the home hero. Cool vibe. Great energy.
Bad idea: loading Three.js + Vanta in the root layout, so every docs page downloaded and parsed it… and never used it.
Fix
Load those scripts only on the home page. Navigate away? Docs stay light. Come back? Scripts can reuse if still in the document.
Rule of thumb: if a page doesn't show the effect, it shouldn't download the engine.Fix #4: Google Fonts was sitting on the critical path
We were @import-ing Cabin from Google Fonts inside CSS. Classic. Also classic: Lighthouse screaming "render-blocking, ~480ms."
Fix
Self-host Cabin with next/font: local woff2, display: swap, no third-party CSS round trip.
Small change. Cleaner first paint. One less dependency on someone else's CDN for text to appear.
Fix #5: Images: Squoosh energy, but incremental
Dropped photos and screenshots were… generous. Like "1.5 MB datacenter aisle photo in a blog figure" generous.
Mermaid diagrams? We keep those as SVG. Vectors are already the right format. Don't rasterize them into mush.
For my dropped rasters (jpg / png / webp):
- Optimize in place (same path, so MDX links never break)
- Content-hash manifest so only new/changed files reprocess
- Max long edge 1920, quality-preserving compression
- Never replace a file with a larger broken result
First pass on this repo:
Drop a new screenshot tomorrow, build shrinks that one file, commit, done. No MDX rewrites. No drama.
The scoreboard (local Lighthouse, core smoke URLs)
Same three URLs we always audit in CI: home, a light docs page, HostKid overview.
Performance score
| URL | First scan | Latest | Gain |
|---|---|---|---|
/ | 56 | 99 | +43 pts |
| Docs overview | 43 | 100 | +57 pts |
| HostKid overview | 42 | 99 | +57 pts |
| Average | ~47 | ~99 | ~+111% relative |
Feel-it metrics (HostKid)
| Metric | First | Latest | Improvement |
|---|---|---|---|
| LCP | ~3.2s | ~1.0s | ~70% faster |
| TBT | ~674ms | ~0ms | basically gone |
| TTI | ~36s | ~1s | ~97% faster |
| Weight | ~49 MB | ~1.2 MB | ~98% lighter |
The handbook stopped feeling like a download manager and started feeling like a website. You click a chapter. It opens. You read. That's the product.
What CI still does (and what it doesn't)
Quick transparency, because I get this question:
- Lighthouse CI audits a smoke set (those 3 cores + any MDX you touched in the PR), not every URL on the site
- It runs against a local static server on the runner, not the live GitHub Pages URL, so we catch regressions from the commit itself
- Deploy to Pages is a separate job. LHCI doesn't block shipping
The checklist if your Next + MDX site feels "heavy"
Steal this in order:
- Never pass Contentlayer
bodyinto layout client components - Search index = build artifact + lazy fetch, not props on every page
- Server-prepare card data; keep home/client bundles hungry and thin
- Don't prefetch routes that own fat client chunks unless you mean it
- Page-only scripts for Three/Vanta/analytics toys
- Self-host fonts
- Incremental image optimize for dropped rasters; leave SVG diagrams alone
Closing screen / like and subscribe energy
Look, performance work isn't glamorous. Nobody asks for "please remove my RSC payload bloat" in a feature request.
But a knowledge base that takes half a minute to wake up is a knowledge base people bounce from. HostKid was the stress test that forced the honesty.
We fixed the boring pipes. The content stayed the same. The experience got honest again.
If you're building something similar and your Lighthouse report looks cursed: start with what you're stuffing into client components. Nine times out of ten, that's the boss fight.
Ship the docs. Then make sure opening them doesn't require a coffee break.Thanks for reading. Now go poke Network tab on your own site. You might not like what you find… but you'll know where to swing.