直到现在,在 monorepo 中使用 shadcn/ui 还是有点麻烦。你可以使用 CLI 添加组件,但你必须管理组件安装的位置,并手动修正导入路径。
通过 CLI 中新增的 monorepo 支持,我们让在 monorepo 中使用 shadcn/ui 变得轻松许多。
CLI 现在能够理解 monorepo 结构,会将组件、依赖和注册依赖安装到正确的路径,并帮你处理导入。
快速入门#
创建一个新的 monorepo 项目#
要创建一个新的 monorepo 项目,运行 init 命令并带上 --monorepo 标志。
pnpm dlx shadcn@latest init --monorepo
然后选择你想使用的模板。
? 选择一个模板 ›
❯ Next.js
Vite
TanStack Start
React Router
Astro这将创建一个包含两个工作区 web 和 ui 的 monorepo 项目,并使用 Turborepo 作为构建系统。
一切都已为你设置好,所以你可以开始向项目添加组件了。
注意:该 monorepo 使用 React 19 和 Tailwind CSS v4。
向项目添加组件#
要向项目添加组件,请在你的应用路径下运行 add 命令。
cd apps/webpnpm dlx shadcn@latest add [COMPONENT]
CLI 会自动判断你添加的组件类型,并将正确的文件安装到正确的路径。
例如,如果你运行 npx shadcn@latest add button,CLI 会将按钮组件安装到 packages/ui 下,并更新 apps/web 中组件的导入路径。
如果你运行 npx shadcn@latest add login-01,CLI 会将 button、label、input 和 card 组件安装到 packages/ui 中,而 login-form 组件则安装到 apps/web/components。
导入组件#
你可以从 @workspace/ui 包中导入组件,写法如下:
import { Button } from "@workspace/ui/components/button"你也可以从 @workspace/ui 包中导入 hooks 和工具函数。
import { useTheme } from "@workspace/ui/hooks/use-theme"
import { cn } from "@workspace/ui/lib/utils"文件结构#
当你创建一个新的 monorepo 项目时,CLI 会创建如下文件结构:
apps
└── web # 你的应用代码放这里。
├── app
│ └── page.tsx
├── components
│ └── login-form.tsx
├── components.json
└── package.json
packages
└── ui # 你的组件和依赖安装于此。
├── src
│ ├── components
│ │ └── button.tsx
│ ├── hooks
│ ├── lib
│ │ └── utils.ts
│ └── styles
│ └── globals.css
├── components.json
└── package.json
package.json
turbo.json要求#
-
每个工作区都必须有一个
components.json文件。package.json文件告诉 npm 如何安装依赖,而components.json文件则告诉 CLI 如何以及在哪里安装组件。 -
components.json文件必须正确定义该工作区的别名。这会告诉 CLI 如何导入组件、hooks、工具等。
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "../../packages/ui/src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"hooks": "@/hooks",
"lib": "@/lib",
"utils": "@workspace/ui/lib/utils",
"ui": "@workspace/ui/components"
}
}{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"iconLibrary": "lucide",
"aliases": {
"components": "@workspace/ui/components",
"utils": "@workspace/ui/lib/utils",
"hooks": "@workspace/ui/hooks",
"lib": "@workspace/ui/lib",
"ui": "@workspace/ui/components"
}
}-
确保两个
components.json文件中的style、iconLibrary和baseColor保持一致。 -
对于 Tailwind CSS v4,
components.json中的tailwind配置需留空。
遵循这些要求后,CLI 就能够将 ui 组件、blocks、libs 和 hooks 安装到正确的路径,并为你处理导入。
package.json#imports 非常适合工作区内的包本地别名,例如 packages/ui 内部。对于共享工作区导入,例如 @workspace/ui/components,请在 components.json 中保留显式别名。CLI 会使用这些别名来跨工作区边界路由文件。
Using package.json#imports#
For monorepos that use package imports and do not rely on tsconfig.json paths, use:
- Local
#...aliases for files within each workspace - The workspace package’s
exportsfor shared imports, such as@workspace/ui/components
For example, an app workspace can use local package imports:
{
"name": "web",
"private": true,
"type": "module",
"imports": {
"#components/*": "./src/components/*.tsx",
"#lib/*": "./src/lib/*.ts",
"#hooks/*": "./src/hooks/*.ts"
},
"dependencies": {
"@workspace/ui": "workspace:*"
}
}{
"aliases": {
"components": "#components",
"ui": "@workspace/ui/components",
"lib": "#lib",
"hooks": "#hooks",
"utils": "@workspace/ui/lib/utils"
}
}The shared UI package can then expose its install targets through exports:
{
"name": "@workspace/ui",
"private": true,
"type": "module",
"imports": {
"#components/*": "./src/components/*.tsx",
"#lib/*": "./src/lib/*.ts",
"#hooks/*": "./src/hooks/*.ts"
},
"exports": {
"./globals.css": "./src/styles/globals.css",
"./components/*": "./src/components/*.tsx",
"./lib/*": "./src/lib/*.ts",
"./hooks/*": "./src/hooks/*.ts"
}
}{
"aliases": {
"components": "#components",
"ui": "#components",
"lib": "#lib",
"hooks": "#hooks",
"utils": "#lib/utils"
}
}In this setup:
- Files added from apps to the shared UI package are routed through
@workspace/ui/... - Files added within
packages/uiuse the package-local#...aliases - Shared packages must export any path that is referenced by another workspace
For framework-specific package import configuration, see the package imports guide.