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