Next.js → TanStack Start migration experiment
Originally a Next.js 15 + shadcn + Firebase app, rebuilt end-to-end on TanStack Start to compare developer experience, bundle size, and real-world performance. Both versions are live and serving the same content side-by-side.
Date
Live ProjectView site

Rebuilding my personal blog with the TanStack ecosystem — a Next.js → TanStack Start migration experiment
Originally a Next.js 15 + shadcn + Firebase app, rebuilt end-to-end on TanStack Start to compare developer experience, bundle size, and real-world performance. Both versions are live and serving the same content side-by-side.
Live deployments
- Next.js version (original): ganesan.dev — Firebase Hosting
- TanStack version (this repo): ganesan-dev-26-tanstack-start.vercel.app — Vercel
The Stack
- TanStack Start + TanStack Router for the app shell, file-based routes, and isomorphic data loading
- React 19 + TypeScript 5.8
- Vite 7 for the build (replacing Turbopack from the Next.js side)
- Nitro v3 with the
vercelpreset for the production serverless bundle - TanStack Query (with Devtools) wired into the router, dehydrated from SSR and reused on the client
- TanStack Form for forms, TanStack Store for shared state, TanStack Table + TanStack Virtual for data-heavy views
- Material UI v9 (with Emotion) for the UI layer
- Firebase Auth + Firestore via
firebase-adminon the server, gated through routebeforeLoadhooks (no separate middleware file) - Zod for validation in both forms and
createServerFnserver functions - Resend for transactional email
@uiw/react-md-editor+ react-markdown + remark-gfm for the post editor and renderer
Architectural highlights
- Type-safe routing —
routeTree.gen.tsmakes broken links a build-time error instead of a runtime surprise - Isomorphic loaders — same loader code runs on the server during initial render and on the client during navigation, no extra waterfalls
createServerFnserver functions living in a dedicatedsrc/server/functions/directory (organised, not scattered as'use server'markers)createAPIFileRouteAPI endpoints for health, contact, auth, analytics, sitemap- Auth in
beforeLoad— protected routes verify the session before rendering, no edge middleware required - No RSC — classic SSR + client React, which kept the deployment story simpler
Build & runtime numbers (vs the Next.js version)
- Client JS: ~1,823 KB — 37% smaller than the Next.js build
- Server JS: ~335 KB — 85% smaller
- Build time: ~25.4 s (19.5 s client + 5.9 s server)
- JS over the wire (mobile): 222 KB vs 326 KB on the Next.js side
- Lighthouse Performance (mobile): 75/100 vs 61/100 on the Next.js side
- TTFB (warm, Vercel edge): ~305 ms vs ~1,390 ms on Firebase Hosting
Read more
- Full write-up with side-by-side comparison and benchmarks: attempt with tanstack start and material ui