I Refactored This Blog for UX, Not Vanity
From server-first rendering to no-JS fallback and cross-browser checks.
Feb 14, 2026
I keep telling teams to optimize for user flow, then realized I was not fully doing it on my own blog.
It was decent. It was readable. It worked for me. But it was still carrying the classic engineering smell: features that were useful in isolation, yet expensive on first load and fragile across edge cases.
So I took one full pass and treated this site like a real product with explicit UX constraints. First render should stay mostly server-first. Core reading should work even when JavaScript is disabled. Browser compatibility should be enforced by tests, not confidence.
That is what this post is about: what changed, why it mattered, and what actually worked.
The UX Tax I Was Quietly Charging
Nothing was catastrophically broken, but there were hidden costs. Too much client-only behavior mounted globally. The homepage feed depended on JavaScript for progressive loading. Analytics and auxiliary listeners were eager. Rendering paths were not explicitly validated in Firefox, WebKit, or no-JS mode.
All of this adds up to a subtle kind of friction. Not enough to trigger a bug report, but enough to reduce perceived speed and resilience.
The Core Decision: Stay Server-First, Enhance Later
I am already on Next.js App Router, so React Server Components are the default. But default does not mean free. You still need to decide where client boundaries live.
The change in mindset was to keep the critical reading path as plain HTML from the server, load interactivity only where it creates clear user value, and treat no-JS mode as a baseline instead of an afterthought.
What Changed in the Code
1) Homepage feed is now server-rendered end to end
I removed the client-side infinite feed dependency from the home page and render the full post list server-side.
This helped because the page now works out of the box without JavaScript. Basic discovery and navigation do not need hydration, and the first interaction carries less client orchestration.
2) Heavy client features moved behind explicit boundaries
Interactive components that are genuinely optional were pushed behind client-only dynamic imports. That included the inline AI helper on article pages, article analytics hooks, and the theme toggle UI.
The reading experience no longer waits for optional behavior.
3) No-JS behavior became intentional
I added explicit JS-only handling for non-essential controls and verified that the core journey still works: browse homepage, open a post, read content, and navigate using footer/header links.
This is progressive enhancement in practice, not in slideware.
4) Analytics was demoted from critical path
Tracking scripts were moved to lazy loading, and some eager global listeners were removed.
The trade-off is straightforward. You might lose a small amount of early-session telemetry. Readers get less main-thread work during the initial interaction.
I will usually take that trade.
5) Mermaid rendering is loaded on demand
A bundling issue around Mermaid was fixed by dynamic loading at render time in the client component.
This improved build reliability for content pages and stopped non-diagram pages from paying for diagram code upfront.
Browser Compatibility Is Now a System, Not a Promise
After code cleanup, I set up a compatibility workflow with Playwright across Chromium, Firefox, WebKit, and Chromium with JavaScript disabled.
I also added a documented browser support policy and no-JS baseline expectations. Now the contract is explicit and testable.
This part matters more than the tool itself. Most UX regressions are not caused by ignorance, they are caused by missing feedback loops.
What Worked (and What I Learned Again)
The biggest lesson did not change:
Speed is less about one clever optimization and more about architectural honesty.
If a feature is optional, load it optionally. If a path is critical, make it server-first. If you claim compatibility, test compatibility.
Also, not every improvement will show up as dramatic bundle-size wins. Some of the biggest gains here are resilience: better no-JS behavior, safer browser coverage, and fewer fragile assumptions in the rendering path.
That is still UX work.
The checklist I would repeat
I would identify which screens must work as static HTML only, move non-critical client logic behind explicit boundaries, add a no-JS browser project in the E2E suite, test Firefox and WebKit every PR, and treat tracking and convenience UI as secondary to reading flow.
This refactor was not glamorous. But it made the blog feel more solid, more predictable, and more respectful of reader context.