Introduction
In April 2024, I wrote an article titled: “What is Shadcn UI? Why is it so popular?,” but looking back, it feels poorly written. Recently, as I have been integrating Vue Shadcn, I want to provide a more comprehensive review of this tool.
If you are considering building a Tailwind-based UI, this article is worth a read.
Frontend Environment Background
Frontend developers are increasingly embracing utility-first CSS solutions, as reflected in the CSS Survey. The most widely used CSS framework is TailwindCSS, but it only addresses the management of “how to write and manage CSS,” essentially enabling rapid development through the generation of predefined utility CSS classes.
UI Expansion Challenges
In contrast to traditional CSS frameworks like Bootstrap, Tailwind does not concern itself with implementing specific styles, such as buttons, input boxes, dialogs, etc. While this provides flexibility and minimal abstraction, it also means that one must create styles from scratch.
On the other hand, traditional component libraries face the issue of being too rigid (or uniform) and challenging to customize, such as Element+. These libraries often require directly selecting corresponding elements to apply styles, leading to potential issues like numerous !important
declarations that may conflict with each other, making the entire project difficult to modify.
::v-deep .el-input { width: 300px; border: none; .el-input__wrapper { background-color: transparent !important; border: none; .el-input__inner { color: skyblue; } }}
Another approach is to modify and compile Sass from the source, like Bootstrap.
// 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
Why mention traditional UI libraries? Because there are cases where developers want to retain the flexibility of Tailwind while also expecting a ready-made component library for rapid development, which leads to a chaotic and hard-to-maintain situation.
How to Maintain Component Flexibility?
Tailwind naturally offers ready-made UI component solutions like Flowbite and Daisy UI, but I find Shadcn to be the most reasonable and user-friendly solution.
- Shadcn is a collection of components based on Radix UI.
- Radix UI focuses on providing accessible, unstyled, and open generic components built with native CSS and React.
- Shadcn wraps Radix by adding appropriate Tailwind styles for immediate use.
In other words, Radix implements most functionalities while Shadcn offers a usable appearance, achieving the separation of “function” and “style.”
Shadcn is Not Just Any Component Library
UI component libraries typically involve installing a specific NPM package and importing it into your project, but Shadcn is not a “component library” but rather a “component collection.”
This means it is essentially a collection of component code, meant to be used by copying and pasting. You can also use the CLI tool to achieve this, but the underlying action remains the same. This design provides greater flexibility and transparency for style modifications, even giving AI better context, but it also means greater responsibility, such as tracking the dependencies used in the distributed components.
The CLI determines the components’ descriptions, locations, types, and dependencies to install through a specific registry.json format. You don’t necessarily have to use their official components’ registry; you can also set up your own, which is essentially a specific JSON format for loading component descriptions.
Shadcn Initialization
When initializing Shadcn through the CLI installation, all settings and preferences will be recorded in a newly generated components.json
file as follows:
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"}
All variables will be initialized in the Tailwind configuration, stored through native CSS variables. The principle is straightforward, dividing colors into “background” and “foreground” categories, along with several semantically useful colors such as primary
, secondary
, muted
, and destructive
.
@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);}
Real-World Examples of Shadcn
For example, Shadcn Vue provides a Tabs component, which you can explore through the links in the documentation:
- Component Source: View the component source code.
- API Reference: Check the underlying Reka component interface format used by Shadcn.
Understanding the overall component allows you to run CLI commands to install relevant components and dependencies:
pnpm dlx shadcn-vue@latest add tabs
In reality, it will pull the official 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" } ] }, ]}
The CLI will automatically install all dependencies and copy all components. If you are not satisfied with the styles provided by Shadcn, you can create your own Shadcn component like WindowTab and modify a copy of Tabs without worrying about affecting the original Tabs.
Tabs/├── Tabs.vue├── TabsContent.vue├── TabsList.vue├── TabsTrigger.vue└── index.tsWindowTab.vue
<script setup lang="ts">import { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs'</script>
More Aspects of Shadcn
You can check Awesome Shadcn UI for related repositories, whether they are extended tools, component collections, or versions for different frontend frameworks. For instance, Shadcn Vue is based on Reka UI, but the overall concept remains the same.
If you want to collaborate with the design side, there are ready-made Figma components and design systems to pay attention to: