# 使用 TanStack Start 进行 SSR > 使用 Vite 在 Nitro 中使用 TanStack Start 实现全栈 React。 ```text [.gitignore] node_modules package-lock.json yarn.lock .DS_Store .cache .env .vercel .output .nitro /build/ /api/ /server/build /public/build# Sentry Config File .env.sentry-build-plugin /test-results/ /playwright-report/ /blob-report/ /playwright/.cache/ .tanstack ``` ```json [package.json] { "type": "module", "scripts": { "build": "vite build", "dev": "vite dev", "start": "node .output/server/index.mjs" }, "dependencies": { "@tanstack/react-router": "^1.158.1", "@tanstack/react-router-devtools": "^1.158.1", "@tanstack/react-start": "^1.158.3", "nitro": "latest", "react": "^19.2.4", "react-dom": "^19.2.4", "tailwind-merge": "^3.4.0", "zod": "^4.3.6" }, "devDependencies": { "@tailwindcss/vite": "^4.1.18", "@types/node": "latest", "@types/react": "^19.2.13", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.3", "tailwindcss": "^4.1.18", "typescript": "^5.9.3", "vite": "beta", "vite-tsconfig-paths": "^6.0.5" } } ``` ```ts [server.ts] import handler, { createServerEntry } from "@tanstack/react-start/server-entry"; export default createServerEntry({ fetch(request) { return handler.fetch(request); }, }); ``` ```json [tsconfig.json] { "extends": "nitro/tsconfig", "compilerOptions": { "baseUrl": ".", "jsx": "react-jsx", "paths": { "~/*": ["./src/*"] } } } ``` ```js [vite.config.mjs] import { defineConfig } from "vite"; import { nitro } from "nitro/vite"; import { tanstackStart } from "@tanstack/react-start/plugin/vite"; import viteReact from "@vitejs/plugin-react"; import viteTsConfigPaths from "vite-tsconfig-paths"; import tailwindcss from "@tailwindcss/vite"; export default defineConfig({ plugins: [ viteTsConfigPaths({ projects: ["./tsconfig.json"] }), tanstackStart(), viteReact(), tailwindcss(), nitro(), ], environments: { ssr: { build: { rollupOptions: { input: "./server.ts" } } }, }, }); ``` ```tsx [src/router.tsx] import { createRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen.ts"; export function getRouter() { const router = createRouter({ routeTree, defaultPreload: "intent", defaultErrorComponent: () =>
Internal Server Error
, defaultNotFoundComponent: () =>
Not Found
, scrollRestoration: true, }); return router; } ``` ```ts [src/routeTree.gen.ts] /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as ApiTestRouteImport } from './routes/api/test' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) const ApiTestRoute = ApiTestRouteImport.update({ id: '/api/test', path: '/api/test', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/api/test': typeof ApiTestRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/api/test': typeof ApiTestRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/api/test': typeof ApiTestRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' | '/api/test' fileRoutesByTo: FileRoutesByTo to: '/' | '/api/test' id: '__root__' | '/' | '/api/test' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute ApiTestRoute: typeof ApiTestRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/api/test': { id: '/api/test' path: '/api/test' fullPath: '/api/test' preLoaderRoute: typeof ApiTestRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, ApiTestRoute: ApiTestRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/react-start' declare module '@tanstack/react-start' { interface Register { ssr: true router: Awaited> } } ``` ```tsx [src/routes/__root.tsx] /// import { HeadContent, Link, Scripts, createRootRoute } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import * as React from "react"; import appCss from "~/styles/app.css?url"; export const Route = createRootRoute({ head: () => ({ meta: [ { charSet: "utf8" }, { name: "viewport", content: "width=device-width, initial-scale=1" }, ], links: [{ rel: "stylesheet", href: appCss }], scripts: [{ src: "/customScript.js", type: "text/javascript" }], }), errorComponent: () =>

500: Internal Server Error

, notFoundComponent: () =>

404: Page Not Found

, shellComponent: RootDocument, }); function RootDocument({ children }: { children: React.ReactNode }) { return (
Home {" "} 404

{children} ); } ``` ```tsx [src/routes/index.tsx] import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ component: Home }); function Home() { return (

Welcome Home!

/api/test
); } ``` ```css [src/styles/app.css] @import "tailwindcss"; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } } @layer base { html { color-scheme: light dark; } * { @apply border-gray-200 dark:border-gray-800; } html, body { @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; } .using-mouse * { outline: none !important; } } ```
通过 TanStack Start 配合 Nitro 搭建一个全栈 React 框架体验,支持服务端渲染、基于文件的路由以及集成的 API 路由。 ## 概览 #### 在 Vite 配置中添加 Nitro 插件 #### 使用 TanStack Start 的服务 handler 创建服务器入口 #### 配置默认组件的路由器 #### 使用基于文件的路由定义页面和 API 路由 ## 1. 配置 Vite 在你的 Vite 配置中添加 Nitro、React、TanStack Start 和 Tailwind 插件: ```js [vite.config.mjs] import { defineConfig } from "vite"; import { nitro } from "nitro/vite"; import { tanstackStart } from "@tanstack/react-start/plugin/vite"; import viteReact from "@vitejs/plugin-react"; import viteTsConfigPaths from "vite-tsconfig-paths"; import tailwindcss from "@tailwindcss/vite"; export default defineConfig({ plugins: [ viteTsConfigPaths({ projects: ["./tsconfig.json"] }), tanstackStart(), viteReact(), tailwindcss(), nitro(), ], environments: { ssr: { build: { rollupOptions: { input: "./server.ts" } } }, }, }); ``` `tanstackStart()` 插件提供完整的 SSR 集成和自动的客户端入口处理。使用 `viteTsConfigPaths()` 可以启用 tsconfig 中的路径别名如 `~/`。`environments.ssr` 指定了服务器入口文件。 ## 2. 创建服务器入口 创建一个使用 TanStack Start handler 的服务器入口: ```ts [server.ts] import handler, { createServerEntry } from "@tanstack/react-start/server-entry"; export default createServerEntry({ fetch(request) { return handler.fetch(request); }, }); ``` TanStack Start 会自动处理 SSR。`createServerEntry` 用于适配 Nitro 的服务器入口格式,`handler.fetch` 处理所有传入请求。 ## 3. 配置路由器 创建一个带有默认错误和未找到组件的路由器工厂函数: ```tsx [src/router.tsx] import { createRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen.ts"; export function getRouter() { const router = createRouter({ routeTree, defaultPreload: "intent", defaultErrorComponent: () =>
内部服务器错误
, defaultNotFoundComponent: () =>
未找到页面
, scrollRestoration: true, }); return router; } ``` 路由器工厂函数配置了预加载行为、滚动恢复及默认错误/未找到组件。 ## 4. 创建根路由 根路由定义 HTML 外壳,包含 head 管理和脚本: ```tsx [src/routes/__root.tsx] /// import { HeadContent, Link, Scripts, createRootRoute } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import * as React from "react"; import appCss from "~/styles/app.css?url"; export const Route = createRootRoute({ head: () => ({ meta: [ { charSet: "utf8" }, { name: "viewport", content: "width=device-width, initial-scale=1" }, ], links: [{ rel: "stylesheet", href: appCss }], scripts: [{ src: "/customScript.js", type: "text/javascript" }], }), errorComponent: () =>

500:内部服务器错误

, notFoundComponent: () =>

404:未找到页面

, shellComponent: RootDocument, }); function RootDocument({ children }: { children: React.ReactNode }) { return (
首页 {" "} 404

{children} ); } ``` 在 `head()` 函数中定义 meta 标签、样式表和脚本。`shellComponent` 是包裹所有页面的 HTML 文档外壳。使用 `HeadContent` 渲染 head 配置,`Scripts` 注入客户端 JavaScript 以进行 hydration。 ## 5. 创建页面路由 页面路由定义应用页面: ```tsx [src/routes/index.tsx] import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ component: Home }); function Home() { return (

欢迎回家!

/api/test
); } ``` ## API 路由 TanStack Start 支持与页面路由并行的 API 路由。在 `src/routes/api/` 下创建文件来定义服务端接口,Nitro 会自动处理这些接口。 ## 了解更多 - [TanStack Start 文档](https://tanstack.com/start) - [服务器入口](/docs/server-entry)