1. Shadcn UI là gì và tại sao nó đặc biệt?
Shadcn UI là một bộ components UI mã nguồn mở ngày càng được ưa chuộng trong cộng đồng frontend. Nhưng khác với các thư viện UI truyền thống như Material UI, Bootstrap hay Chakra UI, Shadcn UI không phải là một "thư viện" theo nghĩa thông thường.
Điều gì khiến Shadcn UI khác biệt? Thay vì cài đặt một package và import components từ đó, Shadcn UI cho phép bạn sao chép code của components trực tiếp vào dự án của mình. Đây là cách tiếp cận "copy-paste" nhưng được tự động hóa thông qua CLI.
Hãy tưởng tượng đơn giản như sau:
- Thư viện UI thông thường: Như việc thuê một căn nhà được thiết kế sẵn. Bạn có thể sử dụng nhưng không thể thay đổi cấu trúc.
- Shadcn UI: Giống như mua bản thiết kế nhà đẹp, nhưng bạn có quyền sở hữu bản thiết kế và có thể tùy ý sửa đổi.
# Thay vì cài đặt thư viện như thông thường
npm install some-ui-library
# Với Shadcn UI, bạn thêm từng component vào dự án
npx shadcn-ui@latest add button
Sau khi chạy lệnh trên, bạn sẽ thấy một file button.tsx
xuất hiện trong thư mục components của dự án. File này chứa toàn bộ code của Button component, và bạn có thể tự do chỉnh sửa nó.
2. Vai trò của Shadcn UI trong phát triển frontend
Shadcn UI giải quyết một vấn đề cốt lõi mà nhiều developers gặp phải: làm sao để vừa phát triển nhanh, vừa có toàn quyền kiểm soát giao diện?
2.1 Trước khi có Shadcn UI
Trước đây, developers thường phải lựa chọn một trong hai cách:
// Cách 1: Dùng thư viện UI (phát triển nhanh, ít kiểm soát)
import { Button } from 'ui-library';
function App() {
return <Button color="primary">Click me</Button>;
// Nhưng khi cần tùy biến đặc biệt? Phải override CSS hoặc hack!
}
// Cách 2: Tự xây dựng mọi thứ (kiểm soát tối đa, nhưng chậm)
function CustomButton({ children, ...props }) {
return (
<button
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
{...props}
>
{children}
</button>
);
}
2.2 Với Shadcn UI
Bạn có được sự cân bằng hoàn hảo:
// components/ui/button.tsx - File này được tạo bởi Shadcn UI CLI
// Nhưng bạn có thể chỉnh sửa nó theo ý muốn!
import * as React from "react";
import { cva } from "class-variance-authority";
// Định nghĩa variants và styles
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
// Thêm một variant mới? Dễ dàng!
custom: "bg-gradient-to-r from-purple-500 to-pink-500 text-white",
},
// ...còn nhiều tùy chọn khác
},
}
);
// Sử dụng trong ứng dụng
import { Button } from "@/components/ui/button";
function App() {
return <Button variant="custom">Beautiful Button</Button>;
}
2.3. Những lợi ích chính
- Sở hữu code: Bạn hoàn toàn kiểm soát mỗi component.
- Tùy biến không giới hạn: Không còn hack CSS hay override phức tạp.
- Bundle size tối ưu: Chỉ dùng những components bạn cần.
- Không lo dependency hell: Không cần lo lắng về xung đột phiên bản.
- Dễ bảo trì: Cập nhật từng component một cách độc lập.
3. Các tính năng cốt lõi của Shadcn UI
3.1 Kiến trúc "Copy-Paste" tiên tiến
Shadcn UI hoạt động dựa trên nguyên tắc "copy-paste cấp cao". Điều này có nghĩa là nó không phải package được cài qua npm, mà là bộ code chất lượng cao được đưa trực tiếp vào dự án của bạn thông qua CLI.
# Khi bạn chạy lệnh này
npx shadcn-ui@latest add dialog
# CLI sẽ tạo các file trong thư mục components/ui của dự án
# - dialog.tsx
# - dialog-content.tsx
# - dialog-description.tsx
# Và bạn hoàn toàn sở hữu những file này!
Shadcn UI kết hợp những công nghệ tốt nhất trong hệ sinh thái frontend:
- Tailwind CSS: Cung cấp styling linh hoạt và hiệu quả
- Radix UI: Đảm bảo accessibility và behavior đúng đắn
- class-variance-authority: Quản lý variants dễ dàng
- TypeScript: Type-safety cho developer experience tốt hơn
3.2 Hệ thống theming mạnh mẽ
Shadcn UI sử dụng CSS variables thông qua Tailwind CSS, cho phép bạn dễ dàng tạo và chuyển đổi giữa các theme.
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* Light theme colors */
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
/* ...các biến khác */
}
.dark {
/* Dark theme colors */
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--primary: 0 0% 98%;
/* ...các biến khác */
}
}
Mọi component trong Shadcn UI đều sử dụng các biến này, vì vậy việc thay đổi theme toàn cục trở nên cực kỳ đơn giản.
3.4. Accessibility tích hợp sẵn
Mỗi component của Shadcn UI đều được xây dựng với accessibility là ưu tiên hàng đầu, nhờ vào nền tảng Radix UI primitives. Điều này đảm bảo các components của bạn:
- Hoạt động chính xác với keyboard navigation
- Tương thích với screen readers
- Tuân thủ WAI-ARIA patterns
- Có đủ contrast ratio
4. Cài đặt và sử dụng Shadcn UI
4.1. Cài đặt trong dự án Next.js
# Tạo dự án Next.js mới (nếu chưa có)
npx create-next-app@latest my-app
# Di chuyển vào thư mục dự án
cd my-app
# Khởi tạo Shadcn UI
npx shadcn-ui@latest init
Trong quá trình khởi tạo, CLI sẽ hỏi một số câu hỏi cấu hình:
Would you like to use TypeScript? Yes
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? app/globals.css
Would you like to use CSS variables? Yes
Where would you like to install the components? components/ui
Configure the import alias for components? Yes
4.2. Thêm components vào dự án
# Thêm một component
npx shadcn-ui@latest add button
# Thêm nhiều components cùng lúc
npx shadcn-ui@latest add card form input label
4.3. Sử dụng component trong code
// app/page.tsx
import { Button } from "@/components/ui/button";
export default function Home() {
return (
<div class="d-flex flex-column align-items-center justify-content-center vh-100">
<h1 class="fs-3 fw-bold mb-4">Shadcn UI Demo</h1>
<div class="d-flex flex-column gap-3">
<Button variant="default">Default Button</Button>
<Button variant="destructive">Destructive Button</Button>
<Button variant="outline">Outline Button</Button>
<Button variant="ghost">Ghost Button</Button>
<Button variant="link">Link Button</Button>
</div>
</div>
);
}
5. Xây dựng ứng dụng demo với Shadcn UI
Hãy cùng mình xây dựng một form đăng nhập đơn giản để thấy Shadcn UI hoạt động trên thực tế.
5.1 Thêm các components cần thiết
npx shadcn-ui@latest add card
npx shadcn-ui@latest add input
npx shadcn-ui@latest add label
npx shadcn-ui@latest add button
5.2. Tạo form đăng nhập
// components/LoginForm.tsx
"use client";
import { useState } from "react";
// Original imports from shadcn
// import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
// import { Input } from "@/components/ui/input";
// import { Label } from "@/components/ui/label";
// import { Button } from "@/components/ui/button";
export default function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log("Login attempted with:", { email, password });
// Xử lý đăng nhập ở đây
};
return (
<div class="card mx-auto card-custom" style="width: 350px;"> {/* Use Bootstrap card */}
<div class="card-header card-header-custom"> {/* Use Bootstrap card-header */}
<h5 class="card-title mb-0">Đăng nhập</h5> {/* Use Bootstrap card-title */}
<p class="card-text text-muted">Đăng nhập để tiếp tục sử dụng ứng dụng.</p> {/* Use Bootstrap card-text */}
</div>
<form onSubmit={handleSubmit}>
<div class="card-body space-y-4"> {/* Use Bootstrap card-body and spacing utilities */}
<div class="mb-3"> {/* Use Bootstrap margin-bottom */}
<label for="email" class="form-label">Email</label> {/* Use Bootstrap form-label */}
<input
id="email"
type="email"
placeholder="[email protected]"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
class="form-control form-control-custom"> {/* Use Bootstrap form-control */}
</input>
</div>
<div class="mb-3"> {/* Use Bootstrap margin-bottom */}
<label for="password" class="form-label">Mật khẩu</label> {/* Use Bootstrap form-label */}
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
class="form-control form-control-custom"> {/* Use Bootstrap form-control */}
</input>
</div>
</div>
<div class="card-footer"> {/* Use Bootstrap card-footer */}
<button type="submit" class="btn btn-primary-custom w-100">Đăng nhập</button> {/* Use Bootstrap btn-primary */}
</div>
</form>
</div>
);
}
5.3. Tùy biến component theo nhu cầu
Giả sử bạn muốn thêm một variant mới cho Button component để sử dụng trong form đăng nhập. Bạn có thể dễ dàng sửa đổi file button.tsx
:
// components/ui/button.tsx (chỉnh sửa file đã được tạo bởi CLI)
import * as React from "react";
import { cva } from "class-variance-authority";
// Thêm variant login-button vào buttonVariants
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium...",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input hover:bg-accent hover:text-accent-foreground",
// Thêm variant mới
"login-button": "bg-blue-600 text-white hover:bg-blue-700 transition-colors",
},
// ...các variants khác
},
}
);
// Sử dụng variant mới trong LoginForm.tsx
// Note: When converting to pure Bootstrap, you'd apply classes directly
/*
<Button type="submit" variant="login-button" className="w-full">
Đăng nhập
</Button>
*/
// In Bootstrap, this would be something like:
/*
<button type="submit" class="btn" style="background-color: #2563eb; color: white;">
Đăng nhập
</button>
*/
// Or if you define a custom btn variant in CSS:
<button type="submit" class="btn btn-login-button w-100">
Đăng nhập
</button>
// With CSS like:
/*
.btn-login-button {
background-color: #2563eb;
color: white;
transition: background-color 0.3s ease;
}
.btn-login-button:hover {
background-color: #1e40af;
}
*/
Đây chính là sức mạnh của Shadcn UI - bạn có thể tùy biến mọi thứ theo nhu cầu của mình!
6. Khi nào nên và không nên dùng Shadcn
Khi nào nên chọn Shadcn UI:
- Bạn cần kiểm soát hoàn toàn về code của components
- Dự án cần nhiều tùy biến đặc biệt
- Bạn muốn tối ưu bundle size
- Bạn đã quen thuộc với Tailwind CSS
- Bạn cần xây dựng design system riêng dựa trên nền tảng vững chắc
Khi nào nên cân nhắc thư viện khác:
- Bạn cần một giải pháp "plug and play" hoàn chỉnh
- Dự án cần triển khai nhanh với ít tùy biến
- Team không quen với việc quản lý code của components
- Bạn cần những components phức tạp mà không muốn xây dựng lại
7. Kết luận
Shadcn UI đại diện cho một phương pháp tiếp cận mới trong phát triển UI. Nó cung cấp sự cân bằng hoàn hảo giữa tốc độ phát triển và khả năng tùy biến - một điều mà các thư viện UI truyền thống thường khó đạt được.