This document analyzes the migration from TanStack Start (apps/ui) to Next.js (apps/next) and examines whether the performance improvements were purely due to the framework switch or additional optimizations made during the migration.
The migration produced significant performance improvements:
| Metric | TanStack Start | Next.js | Improvement | 
|---|---|---|---|
| Performance | 85 | ✅ 100 | +15 points | 
| Accessibility | 85 | 84 | -1 point | 
| Best Practices | 74 | 78 | +4 points | 
| SEO | 80 | ✅ 100 | +20 points | 
| FCP | 0.3s | 0.3s | No change | 
| Speed Index | ❌ 2.4s | ✅ 1.1s | 54% faster | 
| TBT | 50ms | 60ms | +10ms | 
| CLS | 0 | 0 | No change | 
Our analysis reveals that significant optimizations were implemented during the migration beyond just changing frameworks. Here are the specific improvements:
Major Performance Impact on Speed Index
// Regular img tags - no optimization
<img
  src={heroImageDark}
  alt="background" 
  width="3276"
  height="4095"
/>import Image from "next/image";
<Image
  src="/new-hero.png"
  alt="app screen"
  width={2696}
  height={1386}
/>Impact: This single change likely accounts for the dramatic Speed Index improvement (2.4s → 1.1s) due to:
- Automatic image optimization and format conversion
 - Lazy loading with proper placeholders
 - Responsive image serving
 - Better compression algorithms
 
Reduced Unnecessary Network Requests
// Aggressive refetching - always stale
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      staleTime: 0,  // Always refetch
      retry: false,
    },
  },
});
// Router config
defaultPreload: "intent",
defaultPreloadStaleTime: 0,  // No caching// Conservative caching strategy
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      staleTime: 5 * 60 * 1000, // 5 minutes cache
      retry: false,
    },
  },
});Impact: Reduced server load and improved perceived performance by avoiding unnecessary refetches.
Better Resource Loading
// Intent-based preloading only
defaultPreload: "intent",// Explicit prefetching on critical navigation paths
<Link href="/dashboard" prefetch={true}>
<Link href="/models" prefetch={true}>
<Link href="/changelog" prefetch={true}>Impact: Better resource preloading for frequently accessed routes.
Improved Search Engine Optimization
This explains the dramatic SEO improvement (80 → 100)
// apps/ui/src/routes/__root.tsx
head: () => ({
  meta: [
    { name: "title", content: "LLM Gateway" },        // ❌ WRONG!
    { property: "og:title", content: "LLM Gateway" },
  ],
}),// apps/next/src/app/layout.tsx
export const metadata: Metadata = {
  title: "LLM Gateway",  // ✅ Generates proper <title> element
  openGraph: {
    title: "LLM Gateway",
  },
};The Problem: TanStack Start was generating invalid HTML:
<!-- What TanStack Start generated (INVALID) -->
<head>
  <meta name="title" content="LLM Gateway">  <!-- ❌ Not a valid title! -->
  <!-- Missing actual <title> element -->
</head>
<!-- What Next.js generates (VALID) -->
<head>
  <title>LLM Gateway</title>  <!-- ✅ Proper HTML title element -->
  <meta property="og:title" content="LLM Gateway">
</head>Impact: This single fix explains most of the SEO improvement because:
- Search engines prioritize the 
<title>element for ranking - Browser tabs display the 
<title>element content - Screen readers announce the 
<title>element for accessibility - Lighthouse SEO audits check for valid 
<title>elements 
// Static generation for public pages
export async function generateStaticParams() {
  // Pre-generate model pages, provider pages, changelog
}
// Dynamic rendering for authenticated pages
export const dynamic = "force-dynamic";
// Better metadata handling
export const metadata: Metadata = {
  metadataBase: new URL("https://llmgateway.io"),
  title: "LLM Gateway",
  openGraph: { /* ... */ },
  twitter: { /* ... */ },
};Additional Impact: The SEO score improvement (80 → 100) also comes from:
- Automatic meta tag optimization
 - Better SSR/hydration handling
 - Proper Open Graph and Twitter Card generation
 - Automatic sitemap generation
 
More Mature Ecosystem
- Uses Vinxi (newer, experimental build tool)
 - Less mature optimization ecosystem
 - Manual bundle optimization required
 
- Battle-tested Webpack/Turbopack bundling
 - Automatic code splitting
 - Tree shaking optimizations
 - Better caching strategies
 
Enhanced Observability
// Client-side only PostHog
<PostHogProvider apiKey={config.posthogKey}>// Both client and server-side analytics
import { PostHog } from "posthog-node";
// Server-side tracking
const posthogClient = new PostHog(config.posthogKey!, {
  host: config.posthogHost,
  flushAt: 1,
  flushInterval: 0,
});Impact: Better tracking and performance monitoring capabilities.
- Next.js built-in meta tag optimization
 - Better SSR/hydration handling
 - Automatic sitemap generation
 - Superior crawlability
 
- Next.js automatic code splitting
 - Mature bundler optimizations
 - Better caching strategies
 - Optimized runtime
 
- Better TypeScript integration
 - More stable ecosystem
 - Superior debugging tools
 - Better deployment optimizations
 
The migration results demonstrate that this was not just a framework switch but a comprehensive optimization effort that included:
- ✅ Image optimization (biggest impact on Speed Index)
 - ✅ Caching strategy improvements (React Query configs)
 - ✅ Prefetching optimizations
 - ✅ Static generation implementation
 - ✅ Enhanced analytics setup
 - ✅ Bundle optimization through mature tooling
 
TanStack Start is not inherently slower than Next.js. The performance gains came from:
- Next.js providing better built-in optimization primitives
 - The development team implementing performance best practices during migration
 - Access to a more mature ecosystem of optimization tools
 
The combination of framework capabilities + implementation best practices explains the dramatic performance improvements seen in the Lighthouse scores.
- Image optimization is critical for Speed Index performance
 - Caching strategies matter for reducing unnecessary network requests
 - Framework maturity provides better optimization primitives
 - Migration is an opportunity to implement neglected optimizations
 - Holistic approach yields better results than framework switching alone
 
This analysis was conducted by comparing the codebases in apps/ui (TanStack Start) and apps/next (Next.js) to understand the specific technical changes that contributed to the performance improvements.