Somewhere between refreshing Hacker News for the fiftieth time and injecting myself with copium by pretending this counts as 'keeping up with the industry', I found the 512KB Club: a directory of sites whose home pages stay under 512KB. It sounded exactly the kind of arbitrary constraint I get irrationally motivated by, so I decided to join.
Investigating Cloudflare's analytics, my blog was sitting at a surprisingly large 1.52MB of uncompressed assets. The goal was to get under 512KB without turning the blog into a lifeless HTML document, and permit entry to the hallowed institution.
1. Scrapping Big Brother (~550KB)
The first big win was analytics. I had a tiny PostHog client imported in _app.tsx, which pulled posthog-js into
every page just to send events to a new analytics platform I was testing. Removing that client dropped a fairly chunky
analytics SDK from the shared bundle, and now lets me live in blissful ignorance that anyone is actually reading my posts.
2. Slimmer article data on the Homepage (~400KB)
Next.js writes JSON for each statically generated route. Code heavy posts like my Rust borrow checker article produced ~200KB+ JSON blobs.
I split the data into two layers:
export const getPostMetadata = () => [
{ data: { title, date, description, readingTime, ... }, filePath }
];
export const getPosts = () => [
{ ...meta, content }
];export const getPostMetadata = () => [
{ data: { title, date, description, readingTime, ... }, filePath }
];
export const getPosts = () => [
{ ...meta, content }
];Then I:
- Switched
/,/tech, and/miscto usegetPostMetadata(). - Added
prefetch={false}to articleLinks so their JSON doesn't prefetch.
The homepage now downloads just one small index.json file with metadata, not multiple full article payloads.
3. Replacing tiny libraries with code (~20KB)
dayjs was in the bundle solely to format dates as YYYY.MM.DD in a couple of places.
I replaced it with a small helper from Codex:
export const formatDate = (value: string | Date) => {
const d = new Date(value);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}.${m}.${day}`;
};export const formatDate = (value: string | Date) => {
const d = new Date(value);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}.${m}.${day}`;
};4. Swapping JavaScript animation for CSS (~20KB)
The theme toggle used framer-motion to spin the sun/moon icon on tap. Fun library, but expensive for a single interaction.
Using Codex, it was rewritten as:
// TSX
<div className="theme-toggle flex items-center">
<HeaderIcon onClick={...}>
{theme === 'light' ? <RiMoonClearLine /> : <RiSunLine />}
</HeaderIcon>
</div>// TSX
<div className="theme-toggle flex items-center">
<HeaderIcon onClick={...}>
{theme === 'light' ? <RiMoonClearLine /> : <RiSunLine />}
</HeaderIcon>
</div>/* CSS */
.theme-toggle {
transition: transform 0.35s ease-out;
transform-origin: center;
will-change: transform;
}
.theme-toggle:active {
animation: theme-toggle-spin 0.35s ease-out;
}
@keyframes theme-toggle-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}/* CSS */
.theme-toggle {
transition: transform 0.35s ease-out;
transform-origin: center;
will-change: transform;
}
.theme-toggle:active {
animation: theme-toggle-spin 0.35s ease-out;
}
@keyframes theme-toggle-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}5. Miscellaneous tidy‑ups (~50–100KB)
A few smaller changes also helped:
- Making the bundle analyzer optional in
next.config.jsso production builds don't need@next/bundle-analyzerinstalled. - Avoiding extra MDX work on pages that don't use it, such as
rehype-shikicode highlighting.
Individually these were small, but together they knocked another chunk off the JS and config overhead.
Where I ended up
I started at roughly 1.52MB of bundle and shaved it down to around 424KB.
Nothing important went away:
- Dark mode still works, with the spinning theme toggle.
- MDX and syntax highlighting are still there.
- The home page still lists posts with dates, descriptions and reading times.
More importantly, you can now find philp.io on the 512KB Club!