Why I Removed Astro From My Stack (Again)
How Astro's "content-first" defaults taught me that fast metrics aren't the same as respectful user experience — and why TanStack Start is now my one-stack solution.
How Astro's defaults showed me that "content-first" and "user-experience-first" are not the same thing — and why I'm consolidating to TanStack Start for everything.
A few days ago, I wrote about adding Astro to my stack. I was excited. The promise was clean: Astro for content, TanStack Start for apps, Hono for APIs. Each tool in its lane.
I was wrong.
Not about Astro being excellent at static content — it is.
I was wrong about what "content-first" really delivers when users start navigating.
I confused developer convenience with reader comfort.
I confused Lighthouse scores with actual human flow.
And in doing so, I shipped sites that felt broken the moment someone tried to use them like real people do.
The Crack That Broke It
It started innocently. A client portfolio in Astro. Stunning first load. 100 Lighthouse. Everyone congratulated me.
Then I used it — not as the builder, but as a visitor.
Clicking to compare projects. Trying to keep a demo video playing while scanning details.
Every click: full page refresh.
Video stops.
Sidebar scroll resets.
Any in-page state: gone.
It felt like 2005. It felt like the web I'd fought to escape. It felt like WordPress — shiny on metrics, disruptive on usage.
What Metrics Hide
Astro sells "zero JS by default" as performance victory. And it is — for the very first visit.
But here's what no score tells you:
| Metric | What It Measures | What It Completely Ignores |
|---|---|---|
| First Contentful Paint | Initial page arrival | The next 20 internal clicks |
| Cumulative Layout Shift | First-load stability | Every reload flicker after that |
| Performance Score | Lab data on empty cache | Real-user frustration on repeated navigation |
My sites scored perfect.
My users still had a worse experience than any SPA I'd built before.
Because performance isn't only speed — it's continuity.
The SessionStorage Wake-Up Call
The real break came with form data loss.
A simple multi-step quote form across pages. Nothing fancy.
Built with Astro + React islands. "JS only where needed," right?
User goes step 1 → step 2.
SessionStorage wiped. Form progress gone.
This isn't a bug. It's how full-page refreshes work: new browsing context, new session.
Temporary state? Erased.
I wasted days on workarounds:
- localStorage (persists forever, wrong scope)
- URL params (ugly, limited length)
- Server sessions (overkill for client flow)
All to fight the framework's default navigation model.
The Layout Illusion
Astro has "layouts." You wrap pages in headers, sidebars, footers. Looks modern.
It's an illusion.
In TanStack Start (or any router-first framework), layouts persist. Header mounts once. Sidebar keeps scroll. Outlet swaps only content.
In Astro, layouts are templates. Every navigation: full re-render.
Header remounts. Sidebar collapses. Video restarts. Console clears.
Layouts without persistence are just fancy includes.
The Docs Red Flag
I should have noticed sooner.
Astro's own documentation uses full-page refreshes between articles.
The creators — who could add
<ClientRouter />They choose refreshes for their own learning resource.
That tells you everything.
"Zero JS by default" isn't a technical choice. It's a philosophical one so rigid they're willing to degrade their own readers' experience to uphold it.
If they won't make reading about Astro smooth, why trust them to make reading your content smooth?
The Incentives Layer
Full refreshes = new request per navigation.
New request = edge hit.
Edge hits = metrics Cloudflare loves.
Cloudflare acquired Astro (early 2026).
Astro loves Cloudflare.
Both thrive on MPA patterns where every click generates cacheable traffic.
Not conspiracy. Just aligned incentives.
And those incentives favor full refreshes over persistent continuity.
My users pay the price.
The Updated Reality Check
Revisiting the table from last time — with honesty:
| Aspect | TanStack Start | Astro (default) | What Actually Matters |
|---|---|---|---|
| Initial load | SSR + React bundle | Zero JS | Astro wins first paint |
| Subsequent navigation | Instant, no refresh | Full refresh, reset | TanStack wins every click after |
| Layouts / Outlets | True persistence | Re-rendered templates | TanStack keeps continuity |
| SessionStorage / State | Survives across routes | Cleared on nav | TanStack preserves user progress |
| Docs / Learning UX | Smooth, persistent | Disruptive refreshes | TanStack respects readers |
| Mental model | Unified, user-first | Content-first | Content-first ≠ reader-first |
The Question That Mattered
Last time I asked: "Is your content serving the framework?"
Wrong question.
Right one:
Is your framework serving your users, or are your users serving someone's defaults/metrics?
Users don't care about zero JS. They care if:
- The video keeps playing
- Form data survives
- Sidebar stays open
- Site feels built for humans in 2026
Astro defaults fail those tests.
What My Projects Actually Need
| Project Type | User Need | Best Fit |
|---|---|---|
| Portfolio | Smooth browsing, compare without resets | Persistent routing |
| Blog / Docs | Read without losing place/context | No refreshes |
| Marketing | Fluid exploration | Continuity |
| Client tools | Keep state across steps | True layouts |
These aren't "app-only" needs. They're basic 2026 expectations.
The Performance Myth Revisited
"But SPAs ship too much JS!"
Thoughtless SPAs do.
TanStack Start doesn't:
- Code-splitting by route
- Lean router core
- Isomorphic loaders (server/client, no duplication)
- Streaming SSR when needed
I can build fast + persistent.
Astro forces me to choose fast or persistent.
The Consolidation
I'm removing Astro.
Not because it's useless — it's exceptional for pure static landing pages.
Because my projects need more than that.
They need sites that disappear so content shines.
Now:
- TanStack Start for everything (SSR/static + persistent routing)
- Hono for APIs
For content: TanStack's static gen + markdown. Takes setup time.
Worth it for no refreshes, real persistence, respectful UX.
Lessons Hard-Won
- Developer DX ≠ user UX. My quick setup doesn't matter if readers fight refreshes.
- Zero JS is a metric, not morality. A blank page loads fast. That's not victory.
- Layouts that remount aren't layouts. They're templates.
- Docs are the ultimate test. If a framework won't smooth its own docs, they're showing priorities.
- Incentives shape defaults. Follow them — and see who benefits.
Objections I Would Have Made (And Why They Don't Hold)
"Add View Transitions!"
Astro could default it for docs. They don't. Defaults reveal truth.
"Content sites don't need state!"
Scroll position is state. Open accordions are state. Form progress is state. Readers notice when lost.
"But it's MPA by design!"
Exactly. And MPA defaults are disrespectful for learning content in 2026.
Conclusion
I was wrong last time. Not about Astro's tech — about what matters most.
What matters is continuity. Respecting that users build mental context while navigating. Making tech invisible so content breathes.
Astro defaults make tech visible — painfully — with every refresh.
TanStack Start makes tech disappear.
That's the win.
One stack. One model. Defaults that serve users first.
TanStack Start for everything.
Because "content-first" and "user-first" are not synonyms.
And I choose users.
If you're eyeing Astro for docs, blogs, or anything navigable: ask who you're really optimizing for. Your bundle size? Or the person trying to learn without losing their place?
Update March 15, 2026: This replaces my earlier Astro recommendation. Real usage changed my mind. Your mileage may vary — but my readers' experience didn't.