ByteNoteByteNote

字节笔记本

2026年5月3日

TanStack Start - 从 Next.js 迁移指南

API中转
¥120

本文提供从 Next.js App Router 迁移到 TanStack Start 的完整指南,帮助你逐步完成框架切换。

迁移概述

本指南提供从 Next.js App Router 迁移到 TanStack Start 的逐步过程。

前置条件

假设你的项目结构如下:

txt
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public/
│   ├── file.svg
│   └── ...
├── src/
│   └── app/
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.tsx
│       └── page.tsx
└── tsconfig.json

逐步迁移(基础)

步骤 1: 移除 Next.js

卸载 Next.js 并删除相关配置文件:

bash
npm uninstall @tailwindcss/postcss next
rm postcss.config.* next.config.*

步骤 2: 安装必需依赖

TanStack Start 基于 Vite 和 TanStack Router:

bash
npm i @tanstack/react-router @tanstack/react-start

Tailwind CSS 和路径别名解析:

bash
npm i -D vite @vitejs/plugin-react @tailwindcss/vite tailwindcss vite-tsconfig-paths

步骤 3: 更新项目配置

package.json:

json
{
  "type": "module",
  "scripts": {
    "dev": "vite dev",
    "build": "vite build",
    "start": "node .output/server/index.mjs"
  }
}

vite.config.ts:

typescript
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  server: {
    port: 3000,
  },
  plugins: [
    tailwindcss(),
    tsconfigPaths(),
    tanstackStart({
      srcDirectory: 'src',
      router: {
        routesDirectory: 'app',
      },
    }),
    viteReact(),
  ],
})

步骤 4: 适配根布局

创建 src/app/__root.tsx 文件(替代 layout.tsx):

Next.js (layout.tsx):

tsx
import type { Metadata } from "next"

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

TanStack Start (__root.tsx):

tsx
import {
  Outlet,
  createRootRoute,
  HeadContent,
  Scripts,
} from "@tanstack/react-router"
import appCss from "./globals.css?url"

export const Route = createRootRoute({
  head: () => ({
    meta: [
      { charSet: "utf-8" },
      {
        name: "viewport",
        content: "width=device-width, initial-scale=1",
      },
      { title: "TanStack Start Starter" }
    ],
    links: [
      {
        rel: 'stylesheet',
        href: appCss,
      },
    ],
  }),
  component: RootLayout,
})

function RootLayout() {
  return (
    <html lang="en">
      <head>
        <HeadContent />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  )
}

步骤 5: 适配首页

创建 src/app/index.tsx(替代 page.tsx):

tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: Home,
})

function Home() {
  return <main>...</main>
}

步骤 6: 创建路由配置

创建 src/router.tsx

tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
  const router = createRouter({
    routeTree,
    scrollRestoration: true,
  })

  return router
}

步骤 7: 验证迁移

bash
npm run dev

高级迁移步骤

路由概念对比

路由示例Next.jsTanStack Start
根布局src/app/layout.tsxsrc/app/__root.tsx
/ 首页src/app/page.tsxsrc/app/index.tsx
/posts 静态src/app/posts/page.tsxsrc/app/posts.tsx
/posts/[slug] 动态src/app/posts/[slug]/page.tsxsrc/app/posts/$slug.tsx
/posts/[...slug] 捕获src/app/posts/[...slug]/page.tsxsrc/app/posts/$.tsx
/api/endpoint APIsrc/app/api/endpoint/route.tssrc/app/api/endpoint.ts

动态和捕获路由

Next.js:

tsx
export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  return <div>Post: {slug}</div>
}

TanStack Start:

tsx
export const Route = createFileRoute('/app/posts/$slug')({
  component: Page,
})

function Page() {
  const { slug } = Route.useParams()
  return <div>Post: {slug}</div>
}

Server Functions(服务端函数)

Next.js Server Actions:

tsx
'use server'

export const create = async () => {
  return true
}

TanStack Start Server Functions:

tsx
import { createServerFn } from "@tanstack/react-start"

export const create = createServerFn().handler(async () => {
  return true
})

Fetching Data(数据获取)

Next.js:

tsx
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()
  return <ul>...</ul>
}

TanStack Start:

tsx
export const Route = createFileRoute('/')({
  component: Page,
  loader: async () => {
    const res = await fetch('https://api.vercel.app/blog')
    return res.json()
  },
})

function Page() {
  const posts = Route.useLoaderData()
  return <ul>...</ul>
}

迁移检查清单

  • 移除 Next.js 和相关配置
  • 安装 TanStack Start 依赖
  • 更新 vite.config.ts
  • 转换 layout.tsx → __root.tsx
  • 转换 page.tsx → index.tsx
  • 创建 router.tsx
  • 更新动态路由语法
  • 迁移 Links 组件
  • 迁移 Server Actions → Server Functions
  • 迁移数据获取到 loader
  • 测试验证

关键差异

特性Next.jsTanStack Start
文件约定page.tsx, layout.tsxindex.tsx, __root.tsx
动态路由[param]$param
数据获取组件内 asyncloader 函数
服务端函数'use server'createServerFn
链接Link hrefLink to
分享: