首页
/ ui/ui 仓库 templates/start-app 模板解析:TanStack Start + shadcn/ui 项目起步实战

ui/ui 仓库 templates/start-app 模板解析:TanStack Start + shadcn/ui 项目起步实战

2026-09-04 21:16:48作者:宣海椒Queenly

本文基于当前仓库中的 templates/start-app/README.md 展开,完整讲解这个用于快速创建 TanStack Start 项目的模板:它以 React 19 + TypeScript + Vite + Tailwind CSS v4 为技术底座,并预置了 shadcn/ui 的接入约定。读完本文,你将理解该模板的目录结构、构建与路由配置,并掌握在其中添加、引用 shadcn/ui 组件的完整流程。

模板定位与目录结构

templates/start-app/README.md 对模板的定位只有一句话:这是一个基于 TanStack Start、React、TypeScript 和 shadcn/ui 的新项目模板。它面向的是需要 SSR / RSC 能力的全栈 React 应用,而不是纯客户端 SPA——这正是选用 TanStack Start(而非裸 Vite + React)的原因。

模板的完整文件布局如下:

templates/start-app/
├── public/
│   ├── favicon.ico
│   ├── manifest.json
│   └── robots.txt
├── src/
│   ├── lib/
│   │   └── utils.ts          # cn() 类名合并工具函数
│   ├── routes/
│   │   ├── __root.tsx        # 根路由:HTML 外壳 + HeadContent/Scripts
│   │   └── index.tsx         # 首页:已演示 Button 组件用法
│   ├── logo.svg
│   ├── routeTree.gen.ts      # 路由树(由文件路由插件自动生成)
│   ├── router.tsx            # 创建并注册 TanStack Router 实例
│   └── styles.css            # Tailwind v4 入口
├── eslint.config.js
├── package.json
├── pnpm-workspace.yaml
├── tsconfig.json
└── vite.config.ts

两个关键约定值得注意:

  • 文件路由src/routes/ 下的文件即路由,routeTree.gen.ts@tanstack/router-plugin 在构建时自动生成,开发者无需手写路由表;
  • 路径别名@/ 指向 ./src/,这是 shadcn/ui 组件引用约定 @/components/ui/... 能成立的前提。

技术栈与依赖清单

package.json 可以确认模板的技术选型与版本约束:

类别 依赖 说明
框架核心 @tanstack/react-startlatest TanStack Start 运行时,提供 SSR 与文件路由
路由 @tanstack/react-router@tanstack/react-router-devtools@tanstack/react-router-ssr-query 客户端路由、DevTools 面板、SSR 场景下的 Query 支持
UI 工具链 @tailwindcss/vitetailwindcss(均 ^4 Tailwind CSS v4 及其 Vite 插件
React reactreact-dom^19.2.6 React 19
工程化 vite ^8typescript ^6eslint ^9vitest ^4prettier ^3 构建、类型检查、Lint、测试与格式化
DevTools @tanstack/react-devtools@tanstack/devtools-vite 运行时调试面板

几点值得说明:

  • 依赖使用 ^ 范围约束 React、Vite、Tailwind 等稳定依赖,而 TanStack 系列统一标记为 latest,从源码结构看,这类依赖的版本由模板同步机制(见文末章节)滚动更新;
  • devDependencies 中包含 @testing-library/reactjsdomvitest,配合 pnpm testvitest run)可直接在模板内写组件测试;
  • pnpm-workspace.yamlpackages: [] 表明该模板被设计为独立项目使用(不属于任何 monorepo),同时通过 allowBuilds 白名单显式允许 esbuildlightningcssunrs-resolver 等原生依赖执行安装后构建脚本,这是 pnpm v10 对构建脚本的默认管控策略。

构建配置:Vite + Tailwind v4 + 路径别名解析

vite.config.ts 只有十余行,却串起了整个工具链:

import { defineConfig } from "vite"
import { devtools } from "@tanstack/devtools-vite"
import { tanstackStart } from "@tanstack/react-start/plugin/vite"
import viteReact from "@vitejs/plugin-react"
import tailwindcss from "@tailwindcss/vite"

const config = defineConfig({
  resolve: { tsconfigPaths: true },
  plugins: [devtools(), tailwindcss(), tanstackStart(), viteReact()],
})

export default config

逐项拆解:

  1. resolve.tsconfigPaths: true——让 Vite 直接读取 tsconfig.json 中的 paths 配置解析 @/ 别名。这是 shadcn/ui 组件导入路径 @/components/ui/button 能在打包阶段被正确解析的底层机制。
  2. tailwindcss()——Tailwind v4 的 Vite 插件。模板中 src/styles.css 的全部内容就是 @import "tailwindcss"; 一行,体现了 Tailwind v4 免 tailwind.config.js、按导入文件自动扫描内容源的新工作方式。
  3. tanstackStart()——TanStack Start 的 Vite 集成插件,负责文件路由、routeTree.gen.ts 生成与 SSR 构建。
  4. viteReact()——React 19 的官方 Vite 插件,处理 JSX 转换与 HMR。
  5. devtools()——注入 TanStack DevTools 的 Vite 端支持,与根路由中的 TanStackDevtools 组件(见下文)配套。

tsconfig.json 采用 strict: true 全量严格模式,并启用了 noUnusedLocalsnoUnusedParametersverbatimModuleSyntax 等约束;paths 配置如下,它是全模板"组件从哪来、到哪去"的路径锚点:

"paths": {
  "@/*": ["./src/*"]
}

应用入口:路由、根文档与工具函数

路由实例:router.tsx

src/router.tsx 负责创建全局唯一的 Router 实例:

import { createRouter as createTanStackRouter } from "@tanstack/react-router"
import { routeTree } from "./routeTree.gen"

export function getRouter() {
  const router = createTanStackRouter({
    routeTree,
    scrollRestoration: true,
    defaultPreload: "intent",
    defaultPreloadStaleTime: 0,
  })

  return router
}

declare module "@tanstack/react-router" {
  interface Register {
    router: ReturnType<typeof getRouter>
  }
}
  • routeTree 来自自动生成的 routeTree.gen.ts,由 src/routes/ 目录结构派生;
  • scrollRestoration: true 启用前进/后退时的滚动位置恢复;
  • defaultPreload: "intent" 让路由在用户悬停等意图信号出现时就预加载数据,defaultPreloadStaleTime: 0 表示预加载时不做缓存复用,始终取新数据;
  • 文件末尾的 declare module 是 TanStack Router 的类型注册惯例:将 Register.router 锁定为 getRouter() 的返回类型,此后所有 LinkuseNavigate 等 API 都能获得带完整路径感知的类型提示。

根路由:__root.tsx

src/routes/__root.tsx 定义了整个应用的 HTML 外壳(shellComponent),这是 TanStack Start 做 SSR 的关键接缝:

  • head: () => ({...})结构化数据声明 <head> 内容(字符集、viewport、标题,以及通过 appCss 注入 styles.css 的样式表链接),而非手写 <head> 标签;
  • notFoundComponent 提供带 Tailwind 类的 404 页面;
  • RootDocument 组件中,<HeadContent /><Scripts /> 是服务端/客户端的分发点——SSR 时它们渲染服务端产出的 head 内容与脚本,CSR 时由客户端接管;
  • 同时挂载了 TanStackDevtools(右下角定位)与 TanStackRouterDevtoolsPanel 插件,开发时可直接在浏览器内查看路由状态。

首页:index.tsx——README 示例的落地处

src/routes/index.tsx 是模板自带的示例页面,也是 README 中"使用组件"步骤的真实体现:

import { createFileRoute } from "@tanstack/react-router"
import { Button } from "@/components/ui/button"

export const Route = createFileRoute("/")({ component: App })

function App() {
  return (
    <div className="flex min-h-svh p-6">
      <div className="flex max-w-md min-w-0 flex-col gap-4 text-sm leading-loose">
        <div>
          <h1 className="font-medium">Project ready!</h1>
          <p>You may now add components and start building.</p>
          <p>We&apos;ve already added the button component for you.</p>
          <Button className="mt-2">Button</Button>
        </div>
      </div>
    </div>
  )
}

页面通过 @/components/ui/button 引入 Button,并以内联 Tailwind 类展示基础排版。需要注意:从仓库文件清单看,模板本身没有随附 src/components/ui/button.tsx 文件——该导入正是由下一步 README 中的 add button 命令来补齐的,这也解释了 README 把"添加组件"放在最前面的原因。

cn() 工具函数:shadcn/ui 的标准依赖

所有 shadcn/ui 组件内部都会调用 cn() 做条件类名合并,模板已在 src/lib/utils.ts 预置了它的标准实现:

import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"
import type { ClassValue } from "clsx"

export function cn(...inputs: Array<ClassValue>) {
  return twMerge(clsx(inputs))
}

clsx 负责条件拼接,tailwind-merge 负责解决冲突类名的覆盖语义(例如 px-2px-4 同时出现时保留后者)。执行 npx shadcn@latest add button 落盘的组件会引用 @/lib/utils 中的 cn,因此这一文件是模板必须自带的"隐形地基"。

添加组件:npx shadcn@latest add

README 的核心操作指令如下:

npx shadcn@latest add button

这条命令会在当前模板项目目录中,把 shadcn/ui registry 的 button 组件写入 components 目录。结合本模板的路径别名约定(@/*./src/*),实际落盘位置为 src/components/ui/button.tsx。执行完成后,index.tsx 中已有的 import { Button } from "@/components/ui/button" 即可被 Vite 正常解析,pnpm dev 启动后首页会渲染出一个可交互的 Button。

添加任意其他组件(dialoginputcard 等)使用同一命令模式即可,registry 组件之间的依赖(如 dialog 依赖 button)会被 CLI 自动补齐。

使用组件:@/components/ui 导入约定

README 给出的使用方式:

import { Button } from "@/components/ui/button";

该约定之所以在模板中开箱可用,依赖三个前置条件已经就位:

  1. tsconfig.json 声明了 paths: { "@/*": ["./src/*"] }
  2. vite.config.ts 开启 resolve.tsconfigPaths: true,使 Vite 与 TypeScript 的别名解析保持一致;
  3. src/lib/utils.ts 提供组件内部依赖的 cn()

此外,eslint.config.js 基于 @tanstack/eslint-config 复用了 TanStack 官方的 ESLint 规则集,仅放宽了少数与本项目风格冲突的规则(如 import/ordersort-imports)。

本地运行与可用脚本

package.json 提供了一组完整的工程脚本,覆盖开发全流程:

pnpm dev        # vite dev --port 3000,默认在 3000 端口启动开发服务器
pnpm build      # vite build,产出可用于部署的构建产物
pnpm preview   # vite preview,本地预览构建产物
pnpm test       # vitest run
pnpm lint       # eslint
pnpm typecheck  # tsc --noEmit
pnpm format     # prettier --write(含 prettier-plugin-tailwindcss 的类名排序)
pnpm check      # prettier --check

dev 脚本显式指定 --port 3000,避免 Vite 默认 5173 端口与团队习惯冲突;formatcheck 的差异在于前者写回文件、后者仅做校验(适合 CI 门禁)。

模板的分发机制:sync-templates.sh

理解这个模板"从何而来",有助于理解它的版本行为。仓库根目录的 scripts/sync-templates.sh 揭示了模板的同步链路:

  1. 本仓库(monorepo)中的 templates/start-app权威源,改动提交后即被视为模板更新;
  2. 脚本将当前目录内容完整复制到一个通过 git clone --depth 1 拉取的只读镜像仓库(shadcn 组织下的同名仓库),采用"先清空、再全量覆盖"的策略,确保文件的新增、修改、删除都能无残留地同步;
  3. 有差异时自动以 chore: update template 提交并推送。

因此,用户通过脚手架创建项目时拿到的是镜像仓库中的模板快照,而其内容始终与本仓库 templates/start-app 保持一致。这也解释了 package.json 中 TanStack 依赖使用 latest 的原因——模板版本由这条同步链路统一滚动。

小结

templates/start-app 是一个"最小但完整"的 TanStack Start 起步模板:

  • 完整继承 README 的操作路径npx shadcn@latest add button 添加组件 → import { Button } from "@/components/ui/button" 使用组件;
  • 配置层面:Tailwind v4 一行导入、Vite 的 tsconfigPaths 别名解析、严格模式 TypeScript,三者在 vite.config.tstsconfig.json 中形成闭环,保证 shadcn/ui 组件导入约定零配置可用;
  • 运行时层面__root.tsxHeadContent/Scripts 提供 SSR 接缝,router.tsx 完成路由类型注册,src/routes/ 文件路由驱动整个应用;
  • 工程层面pnpm dev/build/preview/test/lint/typecheck 一套脚本即可覆盖本地开发与质量检查。

如果你正在评估"要 SSR 还是纯 SPA",这个模板与同目录下的 Vite 系模板(如 templates/vite-app)构成了一个清晰的对照:前者以 TanStack Start 换取服务端渲染能力,代价是路由与构建链绑定 TanStack 生态。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384