Why Choose Shadcn in 2025?

Shadcn 解決什麼問題?為什麼它是 2025 最好用的前端 UI 方案?

前言

在 2024/4 寫過:「什麼是 Shadcn UI?為什麼在前端圈這麼火爆?」文章,不過如今回顧感覺寫得爛透了,正巧最近又在導入 Vue Shadcn 就來更齊全的回顧這項工具。

如果你在考慮建立基於 Tailwind 的元件庫這篇文章值得一看。

前端環境背景

前端開發者們都逐漸接納 Utility-First 的 CSS 解決方案,如同 CSS Survey🔗 趨勢,其中最大宗使用的 CSS 框架就是 TailwindCSS🔗,但它只解決管理「如何撰寫與管理 CSS」這件事,也就是透過生成事先定義好的 Utility CSS Class 來快速開發與管理。

UI 擴充難題

相較於傳統 CSS 框架如 Bootstrap🔗,Tailwind 並不在乎如何實踐具體的樣式,如:按鈕、輸入框、彈窗……等,雖然幾與了極大的彈性和盡可能少量的抽象,但也意味著要自己打造樣式。

另一方面傳統元件庫面臨過於死板(也可以說是統一),難以客製化的問題,像是 Element+🔗,會需要直接選取對應的元素套用樣式,如果維護時習慣不好可能會爆出一堆 !important 互相覆蓋,最後導致整個專案難以改動。

::v-deep .el-input {
width: 300px;
border: none;
.el-input__wrapper {
background-color: transparent !important;
border: none;
.el-input__inner {
color: skyblue;
}
}
}

還有一種方式是從源頭修改並編譯 Sass 如 Bootstrap🔗

Custom.scss
// Option B: Include parts of Bootstrap
// 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
@import "../node_modules/bootstrap/scss/functions";
// 2. Include any default variable overrides here
// 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets)
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/variables-dark";
// 4. Include any default map overrides here
// 5. Include remainder of required parts
@import "../node_modules/bootstrap/scss/maps";
@import "../node_modules/bootstrap/scss/mixins";
@import "../node_modules/bootstrap/scss/root";
// 6. Include any other optional stylesheet partials as desired; list below is not inclusive of all available stylesheets
@import "../node_modules/bootstrap/scss/utilities";
@import "../node_modules/bootstrap/scss/reboot";
@import "../node_modules/bootstrap/scss/type";
@import "../node_modules/bootstrap/scss/images";
@import "../node_modules/bootstrap/scss/containers";
@import "../node_modules/bootstrap/scss/grid";
@import "../node_modules/bootstrap/scss/helpers";
// ...
// 7. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss`
@import "../node_modules/bootstrap/scss/utilities/api";
// 8. Add additional custom code here

會什麼會提到傳統的 UI 庫?是因為遇過案例是為了保留 Tailwind 的彈性,但又期望現成元件庫快速開發,混在一起用就大爆炸難維護。

問題是如何保持元件彈性?

Tailwind 自然有現成 UI 元件方案如:Flowbite🔗Daisy UI🔗,而其中我認為最合理順手的解決是 Shadcn🔗

  1. Shadcn 是一款基於 Radix UI🔗 的元件合集。
  2. Radix UI 著重在提供無障礙、無裝飾且開放🔗的通用元件,使用原生 CSS 和 React 建構。
  3. Shadcn🔗 在 Radix 之上封裝添加合適的 Tailwind 樣式,即拿即用。

也就是說 Radix 實踐了絕大多數功能,而 Shadcn 包裝出了可用的外觀,實現「功能」與「樣式」的分離。

Shadcn 不是一般的元件庫

UI 元件庫通常是安裝某個 NPM 套件並且引用於你的專案當中,但 Shadcn 並不是「元件庫」而是「元件集」。

意味著它實際上就是一段元件代碼集合,本質就是透過複製貼上元件代碼來使用,也可以用 CLI 工具🔗來達成不過背後還是相同的動作。這樣的設計提供更大的樣式修改彈性與透明度,甚至能賦予 AI 更好更全面的前後文,但也意味著負擔更大的責任,具體來說像是元件是分散的,其中使用的依賴套件與維護要自行追蹤。

CLI 透過特定的 registry.json🔗 格式來決定要安裝的元件描述、位置、種類與依賴,不一定要使用它們官方元件的 registry,也可以自架🔗,實際上就是一個特定的 JSON 格式裝載元件描述。

Shadcn 初始化

透過 CLI 初始化 Shadcn🔗 時會將所有設定與偏好紀錄於新生成的 components.json 檔案如下:

Terminal window
pnpm dlx shadcn@latest init
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "new-york",
"typescript": true,
"tailwind": {
"config": "",
"css": "src/style.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"composables": "@/composables",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib"
},
"iconLibrary": "lucide"
}

所有的變數都會初始化在 Tailwind 設定當中,透過 CSS 原生變數儲存,原則很簡單,分成「背景」與「前景色」,並且有數種語意化用途的顏色如:primarysecondarymuteddestructive

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}

Shadcn 實際案例

舉例 Shadcn Vue 有提供 Tabs🔗 元件,可以透過文件中的連結:

了解整個元件的全貌,就可以下 CLI 指令把相關的元件與依賴都安裝上:

Terminal window
pnpm dlx shadcn-vue@latest add tabs

實際上背後會抓 官方的 registry🔗

{
"files": [
{
"name": "tabs",
"type": "registry:ui",
"dependencies": [
"reka-ui",
"@vueuse/core"
],
"registryDependencies": [],
"files": [
{
"path": "ui/tabs/Tabs.vue",
"type": "registry:ui"
},
{
"path": "ui/tabs/TabsContent.vue",
"type": "registry:ui"
},
{
"path": "ui/tabs/TabsList.vue",
"type": "registry:ui"
},
{
"path": "ui/tabs/TabsTrigger.vue",
"type": "registry:ui"
},
{
"path": "ui/tabs/index.ts",
"type": "registry:ui"
}
]
},
]
}

CLI 預設就會安裝所有依賴與複製貼上所有元件🔗,假設真的不滿意 Shadcn 提供的樣式,大可以自創一個 Shadcn 元件如 WindowTab🔗且複製一份 Tabs 修改,不用擔心影響到原先的 Tabs。

Tabs/
├── Tabs.vue
├── TabsContent.vue
├── TabsList.vue
├── TabsTrigger.vue
└── index.ts
WindowTab.vue
<script setup lang="ts">
import { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs'
</script>

更多面向的 Shadcn

可以到 Awesome Shadcn UI🔗 查看相關的倉庫,不管是延伸的工具、元件集或不同前端框架的版本都有。像是 Shadcn Vue🔗 是基於 Reka UI🔗,但整體概念是相同的。

如果要與設計端配合,也有現成 Figma 元件與設計系統可以留意:

延伸閱讀