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:

What Lighthouse found

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:

MetricFirst scan (HostKid)
Performance42
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.

The smoking gun

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

  1. Sidebar only gets metadata: title, url, slug, order (NavDoc)
  2. Search builds public/search-index.json at build time and lazy-loads it when you open search
  3. 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:

MetricBeforeAfter RSC fix
Performance4272
TTI~36s~3.5s
Page weight~49 MB~2.4 MB
RSC transfer~45 MB~0.4 MB
That was the biggest single win. Everything else was cleanup on top of a healthy foundation.

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:

What that actually means

"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
MetricBeforeAfter
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:

optimize-images (simplified)
$

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

URLFirst scanLatestGain
/5699+43 pts
Docs overview43100+57 pts
HostKid overview4299+57 pts
Average~47~99~+111% relative

Feel-it metrics (HostKid)

MetricFirstLatestImprovement
LCP~3.2s~1.0s~70% faster
TBT~674ms~0msbasically gone
TTI~36s~1s~97% faster
Weight~49 MB~1.2 MB~98% lighter
What we gained in human terms

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
Think of it as a seatbelt, not a full track day on the public internet.

The checklist if your Next + MDX site feels "heavy"

Steal this in order:

  1. Never pass Contentlayer body into layout client components
  2. Search index = build artifact + lazy fetch, not props on every page
  3. Server-prepare card data; keep home/client bundles hungry and thin
  4. Don't prefetch routes that own fat client chunks unless you mean it
  5. Page-only scripts for Three/Vanta/analytics toys
  6. Self-host fonts
  7. 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.