Skip to content

Instantly share code, notes, and snippets.

@tannerlinsley
Created July 29, 2025 19:47
Show Gist options
  • Save tannerlinsley/4f7bde6c8026104f0dc06a122a657621 to your computer and use it in GitHub Desktop.
Save tannerlinsley/4f7bde6c8026104f0dc06a122a657621 to your computer and use it in GitHub Desktop.

TanStack Start to Next.js Migration Analysis

Overview

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.

Performance Results

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

Key Finding: It Wasn't Just a Framework Switch

Our analysis reveals that significant optimizations were implemented during the migration beyond just changing frameworks. Here are the specific improvements:

1. Image Optimization 🖼️

Major Performance Impact on Speed Index

TanStack Start Implementation

// Regular img tags - no optimization
<img
  src={heroImageDark}
  alt="background" 
  width="3276"
  height="4095"
/>

Next.js Implementation

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

2. React Query Configuration ⚡

Reduced Unnecessary Network Requests

TanStack Start Configuration

// 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

Next.js Configuration

// 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.

3. Link Prefetching Strategy 🔗

Better Resource Loading

TanStack Start

// Intent-based preloading only
defaultPreload: "intent",

Next.js

// 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.

4. Static Generation & SEO 🔍

Improved Search Engine Optimization

Critical Title Element Fix

This explains the dramatic SEO improvement (80 → 100)

❌ TanStack Start (Incorrect HTML Standard)

// apps/ui/src/routes/__root.tsx
head: () => ({
  meta: [
    { name: "title", content: "LLM Gateway" },        // ❌ WRONG!
    { property: "og:title", content: "LLM Gateway" },
  ],
}),

✅ Next.js (Correct HTML Standard)

// 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

Next.js Additional SEO Advantages

// 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

5. Build Tool & Bundle Optimization 📦

More Mature Ecosystem

TanStack Start

  • Uses Vinxi (newer, experimental build tool)
  • Less mature optimization ecosystem
  • Manual bundle optimization required

Next.js

  • Battle-tested Webpack/Turbopack bundling
  • Automatic code splitting
  • Tree shaking optimizations
  • Better caching strategies

6. Analytics & Monitoring 📊

Enhanced Observability

TanStack Start

// Client-side only PostHog
<PostHogProvider apiKey={config.posthogKey}>

Next.js

// 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.

Framework-Specific Benefits

SEO Improvements (80 → 100)

  • Next.js built-in meta tag optimization
  • Better SSR/hydration handling
  • Automatic sitemap generation
  • Superior crawlability

Performance Improvements

  • Next.js automatic code splitting
  • Mature bundler optimizations
  • Better caching strategies
  • Optimized runtime

Developer Experience

  • Better TypeScript integration
  • More stable ecosystem
  • Superior debugging tools
  • Better deployment optimizations

Conclusion

The migration results demonstrate that this was not just a framework switch but a comprehensive optimization effort that included:

  1. Image optimization (biggest impact on Speed Index)
  2. Caching strategy improvements (React Query configs)
  3. Prefetching optimizations
  4. Static generation implementation
  5. Enhanced analytics setup
  6. Bundle optimization through mature tooling

Key Takeaway

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.

Lessons Learned

  1. Image optimization is critical for Speed Index performance
  2. Caching strategies matter for reducing unnecessary network requests
  3. Framework maturity provides better optimization primitives
  4. Migration is an opportunity to implement neglected optimizations
  5. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment