Diese Anleitung bietet einen Schritt-für-Schritt-Prozess zur Migration eines Projekts vom Next.js App Router zu TanStack Start. Wir respektieren die leistungsstarken Funktionen von Next.js und möchten diesen Übergang so reibungslos wie möglich gestalten.
Diese Schritt-für-Schritt-Anleitung gibt einen Überblick darüber, wie Sie Ihr Next.js App Router-Projekt zu TanStack Start migrieren können. Das Ziel ist es, Ihnen die grundlegenden Schritte des Migrationsprozesses zu vermitteln, damit Sie diese an Ihre spezifischen Projektanforderungen anpassen können.
Bevor wir beginnen, geht diese Anleitung davon aus, dass Ihre Projektstruktur wie folgt aussieht
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── README.md
├── src
│ └── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
└── tsconfig.json
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── README.md
├── src
│ └── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
└── tsconfig.json
Alternativ können Sie mit dem Klonen der folgenden Starter-Vorlage mitarbeiten
npx gitpick nrjdalal/awesome-templates/tree/main/next.js-apps/next.js-start next.js-start-er
npx gitpick nrjdalal/awesome-templates/tree/main/next.js-apps/next.js-start next.js-start-er
Diese Struktur ist eine grundlegende Next.js-Anwendung, die den App Router verwendet, den wir zu TanStack Start migrieren werden.
Deinstallieren Sie zunächst Next.js und entfernen Sie zugehörige Konfigurationsdateien
npm uninstall @tailwindcss/postcss next
rm postcss.config.* next.config.*
npm uninstall @tailwindcss/postcss next
rm postcss.config.* next.config.*
TanStack Start nutzt Vite und TanStack Router
npm i @tanstack/react-router @tanstack/react-start vite
npm i @tanstack/react-router @tanstack/react-start vite
Für Tailwind CSS und die Auflösung von Importen mithilfe von Pfad-Aliassen
npm i -D @tailwindcss/vite tailwindcss vite-tsconfig-paths
npm i -D @tailwindcss/vite tailwindcss vite-tsconfig-paths
Nachdem Sie die notwendigen Abhängigkeiten installiert haben, aktualisieren Sie Ihre Projektkonfigurationsdateien, damit sie mit TanStack Start funktionieren.
{
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"start": "node .output/server/index.mjs"
}
}
{
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"start": "node .output/server/index.mjs"
}
}
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
server: {
port: 3000,
},
plugins: [
tailwindcss(),
// Enables Vite to resolve imports using path aliases.
tsconfigPaths(),
tanstackStart({
tsr: {
// Specifies the directory TanStack Router uses for your routes.
routesDirectory: 'src/app', // Defaults to "src/routes"
},
}),
],
})
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
server: {
port: 3000,
},
plugins: [
tailwindcss(),
// Enables Vite to resolve imports using path aliases.
tsconfigPaths(),
tanstackStart({
tsr: {
// Specifies the directory TanStack Router uses for your routes.
routesDirectory: 'src/app', // Defaults to "src/routes"
},
}),
],
})
Standardmäßig ist routesDirectory auf src/routes gesetzt. Um die Konsistenz mit den Konventionen des Next.js App Routers beizubehalten, können Sie es stattdessen auf src/app setzen.
TanStack Start verwendet einen Routing-Ansatz, der Remix ähnelt, mit einigen Änderungen zur Unterstützung verschachtelter Strukturen und spezieller Funktionen mithilfe von Tokens. Erfahren Sie mehr darüber im Leitfaden Routing Concepts.
Erstellen Sie anstelle von layout.tsx eine Datei namens __root.tsx im Verzeichnis src/app. Diese Datei dient als Root-Layout für Ihre Anwendung.
- import type { Metadata } from "next" // [!code --]
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from "@tanstack/react-router"
import "./globals.css"
- export const metadata: Metadata = { // [!code --]
- title: "Create Next App", // [!code --]
- description: "Generated by create next app", // [!code --]
- } // [!code --]
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: "utf-8" },
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{ title: "TanStack Start Starter" }
],
}),
component: RootLayout,
})
- export default function RootLayout({ // [!code --]
- children, // [!code --]
- }: Readonly<{ // [!code --]
- children: React.ReactNode // [!code --]
- }>) { // [!code --]
- return ( // [!code --]
- <html lang="en"> // [!code --]
- <body>{children}</body> // [!code --]
- </html> // [!code --]
- ) // [!code --]
- } // [!code --]
function RootLayout() {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
)
}
- import type { Metadata } from "next" // [!code --]
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from "@tanstack/react-router"
import "./globals.css"
- export const metadata: Metadata = { // [!code --]
- title: "Create Next App", // [!code --]
- description: "Generated by create next app", // [!code --]
- } // [!code --]
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: "utf-8" },
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{ title: "TanStack Start Starter" }
],
}),
component: RootLayout,
})
- export default function RootLayout({ // [!code --]
- children, // [!code --]
- }: Readonly<{ // [!code --]
- children: React.ReactNode // [!code --]
- }>) { // [!code --]
- return ( // [!code --]
- <html lang="en"> // [!code --]
- <body>{children}</body> // [!code --]
- </html> // [!code --]
- ) // [!code --]
- } // [!code --]
function RootLayout() {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
)
}
Erstellen Sie anstelle von page.tsx eine index.tsx-Datei für die Route /.
- export default function Home() { // [!code --]
+ export const Route = createFileRoute('/')({ // [!code ++]
+ component: Home, // [!code ++]
+ }) // [!code ++]
+ function Home() { // [!code ++]
return (
<main className="min-h-dvh w-screen flex items-center justify-center flex-col gap-y-4 p-4">
<img
className="max-w-sm w-full"
src="https://raw.githubusercontent.com/tanstack/tanstack.com/main/src/images/splash-dark.png"
alt="TanStack Logo"
/>
<h1>
<span className="line-through">Next.js</span> TanStack Start
</h1>
<a
className="bg-foreground text-background rounded-full px-4 py-1 hover:opacity-90"
href="https://tanstack.de/start/latest"
target="_blank"
>
Docs
</a>
</main>
)
}
- export default function Home() { // [!code --]
+ export const Route = createFileRoute('/')({ // [!code ++]
+ component: Home, // [!code ++]
+ }) // [!code ++]
+ function Home() { // [!code ++]
return (
<main className="min-h-dvh w-screen flex items-center justify-center flex-col gap-y-4 p-4">
<img
className="max-w-sm w-full"
src="https://raw.githubusercontent.com/tanstack/tanstack.com/main/src/images/splash-dark.png"
alt="TanStack Logo"
/>
<h1>
<span className="line-through">Next.js</span> TanStack Start
</h1>
<a
className="bg-foreground text-background rounded-full px-4 py-1 hover:opacity-90"
href="https://tanstack.de/start/latest"
target="_blank"
>
Docs
</a>
</main>
)
}
Bevor Sie den Entwicklungsserver ausführen können, müssen Sie eine Router-Datei erstellen, die das Verhalten von TanStack Router innerhalb von TanStack Start definiert.
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
🧠 Hier können Sie alles konfigurieren, von der Standard-Vorabladefunktion bis hin zur Aktualität des Cachings.
Machen Sie sich keine Sorgen, wenn Sie zu diesem Zeitpunkt einige TypeScript-Fehler sehen; der nächste Schritt wird diese beheben.
Entwicklungsserver ausführen
npm run dev
npm run dev
Besuchen Sie dann https://:3000. Sie sollten die TanStack Start-Willkommensseite mit ihrem Logo und einem Dokumentationslink sehen.
Wenn Probleme auftreten, überprüfen Sie die obigen Schritte und stellen Sie sicher, dass Dateinamen und Pfade exakt übereinstimmen. Eine Referenzimplementierung finden Sie im Repository nach der Migration.
Nachdem Sie die grundlegende Struktur Ihrer Next.js-Anwendung nach TanStack Start migriert haben, können Sie weitere fortgeschrittene Funktionen und Konzepte erkunden.
| Routenbeispiel | Next.js | TanStack Start |
|---|---|---|
| Root-Layout | src/app/layout.tsx | src/app/__root.tsx |
| / (Startseite) | src/app/page.tsx | src/app/index.tsx |
| /posts (Statische Route) | src/app/posts/page.tsx | src/app/posts.tsx |
| /posts/[slug] (Dynamisch) | src/app/posts/[slug]/page.tsx | src/app/posts/$slug.tsx |
| /posts/[...slug] (Catch-All) | src/app/posts/[...slug]/page.tsx | src/app/posts/$.tsx |
| /api/endpoint (API-Route) | src/app/api/endpoint/route.ts | src/app/api/endpoint.ts |
Erfahren Sie mehr über die Routing Concepts.
Das Abrufen dynamischer Routenparameter in TanStack Start ist unkompliziert.
- export default async function Page({ // [!code --]
- params, // [!code --]
- }: { // [!code --]
- params: Promise<{ slug: string }> // [!code --]
- }) { // [!code --]
+ export const Route = createFileRoute('/app/posts/$slug')({ // [!code ++]
+ component: Page, // [!code ++]
+ }) // [!code ++]
+ function Page() { // [!code ++]
- const { slug } = await params // [!code --]
+ const { slug } = Route.useParams() // [!code ++]
return <div>My Post: {slug}</div>
}
- export default async function Page({ // [!code --]
- params, // [!code --]
- }: { // [!code --]
- params: Promise<{ slug: string }> // [!code --]
- }) { // [!code --]
+ export const Route = createFileRoute('/app/posts/$slug')({ // [!code ++]
+ component: Page, // [!code ++]
+ }) // [!code ++]
+ function Page() { // [!code ++]
- const { slug } = await params // [!code --]
+ const { slug } = Route.useParams() // [!code ++]
return <div>My Post: {slug}</div>
}
Hinweis: Wenn Sie eine Catch-All-Route erstellt haben (wie src/app/posts/$.tsx), können Sie über const { _splat } = Route.useParams() auf die Parameter zugreifen.
Ähnlich können Sie mit const { page, filter, sort } = Route.useSearch() auf searchParams zugreifen.
Erfahren Sie mehr über die Dynamischen und Catch-All-Routen.
- import Link from "next/link" // [!code --]
+ import { Link } from "@tanstack/react-router" // [!code ++]
function Component() {
- return <Link href="/dashboard">Dashboard</Link> // [!code --]
+ return <Link to="/dashboard">Dashboard</Link> // [!code ++]
}
- import Link from "next/link" // [!code --]
+ import { Link } from "@tanstack/react-router" // [!code ++]
function Component() {
- return <Link href="/dashboard">Dashboard</Link> // [!code --]
+ return <Link to="/dashboard">Dashboard</Link> // [!code ++]
}
Erfahren Sie mehr über die Links.
- 'use server' // [!code --]
+ import { createServerFn } from "@tanstack/react-start" // [!code ++]
- export const create = async () => { // [!code --]
+ export const create = createServerFn().handler(async () => { // [!code ++]
return true
- } // [!code --]
+ }) // [!code ++]
- 'use server' // [!code --]
+ import { createServerFn } from "@tanstack/react-start" // [!code ++]
- export const create = async () => { // [!code --]
+ export const create = createServerFn().handler(async () => { // [!code ++]
return true
- } // [!code --]
+ }) // [!code ++]
Erfahren Sie mehr über die Serverfunktionen.
- export async function GET() { // [!code --]
+ export const ServerRoute = createServerFileRoute().methods({ // [!code ++]
+ GET: async () => { // [!code ++]
return Response.json("Hello, World!")
}
+ }) // [!code ++]
- export async function GET() { // [!code --]
+ export const ServerRoute = createServerFileRoute().methods({ // [!code ++]
+ GET: async () => { // [!code ++]
return Response.json("Hello, World!")
}
+ }) // [!code ++]
Erfahren Sie mehr über die Server-Routen.
- import { Inter } from "next/font/google" // [!code --]
- const inter = Inter({ // [!code --]
- subsets: ["latin"], // [!code --]
- display: "swap", // [!code --]
- }) // [!code --]
- export default function Page() { // [!code --]
- return <p className={inter.className}>Font Sans</p> // [!code --]
- } // [!code --]
- import { Inter } from "next/font/google" // [!code --]
- const inter = Inter({ // [!code --]
- subsets: ["latin"], // [!code --]
- display: "swap", // [!code --]
- }) // [!code --]
- export default function Page() { // [!code --]
- return <p className={inter.className}>Font Sans</p> // [!code --]
- } // [!code --]
Verwenden Sie anstelle von next/font den CSS-basierten Ansatz von Tailwind CSS. Installieren Sie Schriftarten (z. B. von Fontsource)
npm i -D @fontsource-variable/dm-sans @fontsource-variable/jetbrains-mono
npm i -D @fontsource-variable/dm-sans @fontsource-variable/jetbrains-mono
Fügen Sie Folgendes zu src/app/globals.css hinzu
@import 'tailwindcss';
@import '@fontsource-variable/dm-sans'; /* [!code ++] */
@import '@fontsource-variable/jetbrains-mono'; /* [!code ++] */
@theme inline {
--font-sans: 'DM Sans Variable', sans-serif; /* [!code ++] */
--font-mono: 'JetBrains Mono Variable', monospace; /* [!code ++] */
/* ... */
}
/* ... */
@import 'tailwindcss';
@import '@fontsource-variable/dm-sans'; /* [!code ++] */
@import '@fontsource-variable/jetbrains-mono'; /* [!code ++] */
@theme inline {
--font-sans: 'DM Sans Variable', sans-serif; /* [!code ++] */
--font-mono: 'JetBrains Mono Variable', monospace; /* [!code ++] */
/* ... */
}
/* ... */
- export default async function Page() { // [!code --]
+ export const Route = createFileRoute('/')({ // [!code ++]
+ component: Page, // [!code ++]
+ loader: async () => { // [!code ++]
+ const res = await fetch('https://api.vercel.app/blog') // [!code ++]
+ return res.json() // [!code ++]
+ }, // [!code ++]
+ }) // [!code ++]
+ function Page() { // [!code ++]
- const data = await fetch('https://api.vercel.app/blog') // [!code --]
- const posts = await data.json() // [!code --]
+ const posts = Route.useLoaderData() // [!code ++]
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
- export default async function Page() { // [!code --]
+ export const Route = createFileRoute('/')({ // [!code ++]
+ component: Page, // [!code ++]
+ loader: async () => { // [!code ++]
+ const res = await fetch('https://api.vercel.app/blog') // [!code ++]
+ return res.json() // [!code ++]
+ }, // [!code ++]
+ }) // [!code ++]
+ function Page() { // [!code ++]
- const data = await fetch('https://api.vercel.app/blog') // [!code --]
- const posts = await data.json() // [!code --]
+ const posts = Route.useLoaderData() // [!code ++]
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.