Đối với các nhà phát triển web, việc tìm kiếm bộ công cụ UI hoàn hảo là một nỗ lực không ngừng. Trong nhiều năm, các nhà phát triển React đã dựa vào các thư viện thành phần truyền thống như Material-UI (MUI), Ant Design và Chakra UI. Các thư viện này cung cấp vô số thành phần được xây dựng sẵn, hứa hẹn tăng tốc độ phát triển. Tuy nhiên, chúng thường đi kèm với một sự đánh đổi: thiếu kiểm soát, ghi đè kiểu dáng (style overrides) cảm giác như một cuộc chiến, và kích thước gói (bundle sizes) bị phình to.
Hãy chào đón Shadcn UI, một cách tiếp cận thay đổi mô hình đã gây bão trong cộng đồng React. Nó không phải là một thư viện thành phần theo cách bạn đã quen thuộc; nó là thứ gì đó tốt hơn. Đó là một bộ sưu tập các thành phần được thiết kế đẹp mắt, dễ tiếp cận (accessible) và có thể tái sử dụng vô tận mà bạn không cài đặt từ npm như một dependency—bạn sao chép chúng trực tiếp vào dự án của mình.
Hướng dẫn toàn diện, dài 4000 từ này sẽ là cẩm nang xác định của bạn, đưa bạn từ người mới bắt đầu hoàn toàn trở thành người sử dụng Shadcn UI tự tin. Chúng ta sẽ khám phá triết lý nền tảng của nó, đi sâu vào thiết lập chi tiết, xây dựng các giao diện phức tạp, nắm vững chủ đề nâng cao và xử lý biểu mẫu, và thảo luận về các thực hành tốt nhất cho các ứng dụng quy mô lớn. Hãy chuẩn bị để suy nghĩ lại về những gì bạn mong đợi từ một bộ công cụ UI.
Bạn muốn một nền tảng tích hợp, Tất cả trong một để Nhóm Phát triển của bạn làm việc cùng nhau với năng suất tối đa?
Apidog đáp ứng mọi yêu cầu của bạn và thay thế Postman với mức giá phải chăng hơn nhiều!
Triết lý Shadcn UI - Một Cách Xây Dựng Mới
Trước khi viết dòng mã đầu tiên, điều quan trọng nhất là phải hiểu tại sao Shadcn UI tồn tại và nó giải quyết những vấn đề gì. Nắm bắt triết lý cốt lõi này là chìa khóa để mở khóa toàn bộ tiềm năng của nó.
Shadcn UI Không Phải Là Gì
- Nó không phải là một gói npm truyền thống. Bạn sẽ không tìm thấy
shadcn-ui
trong danh sách dependencies củapackage.json
. Đây là điểm khác biệt quan trọng nhất. - Nó không phải là một thư viện nguyên khối. Nó không buộc bạn phải cài đặt hàng trăm thành phần khi bạn chỉ cần một nút và một trường nhập liệu.
- Nó không hạn chế. Bạn không bao giờ bị khóa vào một thẩm mỹ thiết kế cụ thể hoặc bị giới hạn bởi khả năng tạo chủ đề (theming) được cung cấp bởi người duy trì thư viện.
Shadcn UI Là Gì
- Một Bộ Sưu Tập Mã Có Thể Tái Sử Dụng: Hãy nghĩ về nó như một bộ công thức được tuyển chọn chuyên nghiệp. Bạn chọn công thức mình muốn (ví dụ: một thành phần
Card
), và hướng dẫn (mã) được cung cấp cho bạn để nấu trong nhà bếp của riêng bạn (dự án của bạn). - Một Cam Kết Về Quyền Sở Hữu Mã: Khi bạn thêm một thành phần Shadcn, mã nguồn của nó—một tệp
.tsx
—được đặt trực tiếp vào cơ sở mã của bạn, thường nằm dướicomponents/ui/
. Giờ đây, nó là thành phần của bạn. Bạn có thể thay đổi cấu trúc, kiểu dáng, logic của nó—bất cứ điều gì. Điều này loại bỏ trải nghiệm khó chịu khi phải vật lộn với các ghi đè CSS!important
hoặc các API prop phức tạp để đạt được một chỉnh sửa hình ảnh đơn giản. - Được Xây Dựng Trên Nền Tảng Hiện Đại, Vững Chắc: Shadcn UI không phát minh lại bánh xe. Nó đứng trên vai những người khổng lồ:
- Tailwind CSS: Một framework CSS ưu tiên tiện ích (utility-first) cung cấp các khối xây dựng cấp thấp để tạo ra bất kỳ thiết kế nào trực tiếp trong mã đánh dấu của bạn. Các thành phần Shadcn được tạo kiểu độc quyền bằng Tailwind, giúp chúng cực kỳ dễ tùy chỉnh nếu bạn quen thuộc với framework này.
- Radix UI: Một thư viện các primitive UI cấp thấp, không có kiểu dáng, dễ tiếp cận (accessible). Radix xử lý tất cả các khía cạnh phức tạp và thường bị bỏ qua của các thành phần UI, chẳng hạn như điều hướng bàn phím, quản lý tiêu điểm (focus management) và các thuộc tính ARIA cho khả năng tiếp cận (a11y). Shadcn lấy những primitive mạnh mẽ, không đầu (headless) này và thêm kiểu dáng đẹp mắt bằng Tailwind CSS.
Ưu điểm chính của mô hình này là sự kết hợp giữa tốc độ và kiểm soát. Bạn có được tốc độ ban đầu khi sử dụng các thành phần được xây dựng sẵn mà không phải hy sinh tính linh hoạt và khả năng bảo trì lâu dài đến từ việc sở hữu mã của riêng bạn.
Thiết Lập Sân Khấu - Thiết Lập Dự Án và Cài Đặt
Hãy chuyển từ lý thuyết sang thực hành. Chúng ta sẽ thiết lập một dự án mới từ đầu. Đối với hướng dẫn này, chúng ta sẽ chủ yếu sử dụng Next.js, vì các thành phần máy chủ (server components) và định tuyến dựa trên tệp của nó phù hợp hoàn hảo với triết lý Shadcn UI. Chúng ta cũng sẽ nói ngắn gọn về thiết lập cho Vite.
Bước 1: Các Yêu Cầu Tiên Quyết Về Môi Trường
Đảm bảo môi trường phát triển của bạn đã sẵn sàng. Bạn sẽ cần:
- Node.js: Phiên bản Hỗ trợ Dài hạn (LTS) mới nhất được khuyến nghị. Bạn có thể tải xuống từ trang web chính thức của Node.js.
- Một Trình Quản lý Gói: Hướng dẫn này sẽ sử dụng
npm
, được đi kèm với Node.js. Bạn cũng có thể sử dụngyarn
hoặcpnpm
.
Bước 2: Tạo Một Ứng Dụng Next.js Mới
Mở terminal của bạn và thực thi lệnh sau để khởi tạo một dự án Next.js mới.Bash
npx create-next-app@latest my-pro-shadcn-app --typescript --tailwind --eslint
Lệnh này tạo cấu trúc một ứng dụng mới trong thư mục có tên my-pro-shadcn-app
. Chúng ta đã bao gồm một số cờ quan trọng:
--typescript
: Shadcn UI được viết bằng TypeScript và hoạt động tốt nhất trong môi trường TypeScript.--tailwind
: Tailwind CSS là một dependency cứng cho việc tạo kiểu của Shadcn UI.--eslint
: Luôn là một thực hành tốt để duy trì chất lượng mã.
Trình cài đặt sẽ hỏi bạn một vài câu hỏi. Đây là các lựa chọn được khuyến nghị cho thiết lập Next.js 14+ hiện đại:
✔ Would you like to use `src/` directory? … No / **Yes**
✔ Would you like to use App Router? (recommended) … No / **Yes**
✔ Would you like to customize the default import alias? … **No** / Yes
Sử dụng App Router là thực hành tiêu chuẩn, và thư mục src/
giúp tổ chức mã. Sau khi hoàn thành, điều hướng vào dự án mới của bạn:Bash
cd my-pro-shadcn-app
Bước 3: Lệnh init
- Đưa Shadcn UI Vào Hoạt Động
Đây là bước quan trọng nhất. Shadcn UI cung cấp một công cụ CLI để cấu hình dự án của bạn. Chạy lệnh sau từ thư mục gốc của dự án của bạn:Bash
npx shadcn-ui@latest init
Điều này sẽ kích hoạt một bảng câu hỏi tương tác để thiết lập dự án của bạn. Hãy phân tích từng câu hỏi và ý nghĩa của nó:
- Bạn có muốn sử dụng TypeScript (khuyến nghị)?
Yes
. Chúng ta đang ở trong một dự án TypeScript. - Bạn muốn sử dụng kiểu nào?
Default
vs.New York
. Đây là hai kiểu hình ảnh được định nghĩa trước.Default
rộng rãi hơn một chút, trong khiNew York
nhỏ gọn hơn. Bạn có thể xem ví dụ trên trang web Shadcn UI. Hãy chọnDefault
. - Bạn muốn sử dụng màu nào làm màu cơ bản? Điều này thiết lập bảng màu chính cho UI của bạn. Mặc định là
Slate
. Hãy giữ nguyênSlate
bây giờ; chúng ta sẽ học cách thay đổi điều này sau. - Tệp
global.css
của bạn ở đâu? CLI phát hiện chính xác nó ởsrc/app/globals.css
. Tệp này là nơi các biến CSS cốt lõi cho việc tạo chủ đề sẽ được chèn vào. - Bạn có muốn sử dụng các biến CSS cho việc tạo chủ đề không?
Yes
. Đây là nền tảng của hệ thống tạo chủ đề của Shadcn, cho phép thay đổi động (như chế độ sáng/tối) và tùy chỉnh dễ dàng. - Tệp
tailwind.config.ts
của bạn nằm ở đâu? CLI phát hiệnsrc/tailwind.config.ts
. Tệp này sẽ được sửa đổi để tích hợp các cài đặt chủ đề của Shadcn. - Cấu hình import alias cho các thành phần:
@/components
. Đây là một thực hành tốt. Điều này có nghĩa là bất kể một tệp nằm sâu đến mức nào, bạn luôn có thể import một thành phần bằng một đường dẫn sạch nhưimport { Button } from "@/components/ui/button";
. - Cấu hình import alias cho utils:
@/lib/utils
. Tương tự như trên, cho các hàm tiện ích. - Bạn có đang sử dụng React Server Components không?
Yes
. Chúng ta đã chọn App Router, sử dụng Server Components theo mặc định. - Ghi cấu hình vào
components.json
?Yes
. Điều này tạo ra một tệp quan trọng ghi nhớ tất cả các lựa chọn của bạn, vì vậy bạn không phải trả lời các câu hỏi này mỗi khi chạynpx shadcn-ui@latest add ...
.
Sau khi bạn xác nhận, CLI sẽ thực hiện công việc của nó:
- Cài đặt Dependencies: Nó thêm các gói cần thiết như
tailwindcss-animate
vàclass-variance-authority
. - Tạo
components.json
: Lưu trữ các lựa chọn cấu hình của bạn. - Cập nhật
tailwind.config.ts
: Chèn plugin Shadcn UI và cấu hình tạo chủ đề. - Cập nhật
globals.css
: Thêm một khối lớn các biến CSS định nghĩa toàn bộ bảng màu, bán kính bo tròn (border radii), và nhiều hơn nữa. - Tạo
lib/utils.ts
: Tệp này xuất một hàm trợ giúpcn
, một tiện ích thông minh để hợp nhất có điều kiện các lớp Tailwind CSS.
Dự án của bạn giờ đây đã được cấu hình đầy đủ.
(Thay thế: Thiết lập Vite)
Nếu bạn đang sử dụng Vite với React, quy trình tương tự. Sau khi thiết lập dự án Vite + React + TS, bạn sẽ cài đặt Tailwind CSS theo cách thủ công và sau đó chạy npx shadcn-ui@latest init. CLI đủ thông minh để phát hiện thiết lập Vite và sẽ hỏi các câu hỏi hơi khác về vị trí tệp (ví dụ: index.css thay vì globals.css).
Xây Dựng UI - Từ Các Thành Phần Đơn Giản Đến Các Bố Cục Phức Tạp
Với việc thiết lập đã hoàn thành, hãy bắt đầu xây dựng. Quy trình làm việc cốt lõi là: xác định nhu cầu, thêm thành phần, sử dụng nó.
Bước 4: Thêm và Sử Dụng Các Thành Phần Đầu Tiên Của Bạn
Hãy dọn dẹp mã boilerplate mặc định của Next.js và xây dựng một giao diện đơn giản.
1. Thêm một Nút (Button):Bash
npx shadcn-ui@latest add button
Quan sát điều gì xảy ra: một tệp mới, src/components/ui/button.tsx
, được tạo. Đây là nút của bạn. Bạn sở hữu nó.
2. Thêm một Thẻ (Card):Bashnpx shadcn-ui@latest add card
Lệnh này thú vị hơn. Nó tạo ra src/components/ui/card.tsx
. Nếu bạn kiểm tra tệp này, bạn sẽ thấy nó xuất nhiều thành phần: Card
, CardHeader
, CardTitle
, CardDescription
, CardContent
, và CardFooter
. Đây là một mẫu phổ biến cho các thành phần phức hợp (compound components).
3. Xây Dựng UI:
Bây giờ, mở src/app/page.tsx
và thay thế nội dung của nó bằng đoạn mã sau:TypeScript
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input"; // Chúng ta sẽ thêm cái này tiếp theo
import { Label } from "@/components/ui/label"; // Và cái này
export default function Home() {
return (
<main className="flex min-h-screen items-center justify-center bg-background p-8">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-2xl">Create Project</CardTitle>
<CardDescription>
Deploy your new project in one-click.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Name of your project" />
</div>
<div className="grid gap-2">
<Label htmlFor="framework">Framework</Label>
{/* Chúng ta sẽ thay thế cái này bằng thành phần Select sau */}
<Input id="framework" placeholder="e.g. Next.js" />
</div>
</CardContent>
<CardFooter>
<Button className="w-full">Deploy</Button>
</CardFooter>
</Card>
</main>
);
}
Mã của chúng ta sẽ chưa chạy vì chúng ta thiếu các thành phần Input
và Label
. Hãy thêm chúng:Bash
npx shadcn-ui@latest add input
npx shadcn-ui@latest add label
Bây giờ, chạy máy chủ phát triển của bạn:Bash
npm run dev
Điều hướng đến http://localhost:3000
. Bạn sẽ thấy một biểu mẫu gọn gàng, chuyên nghiệp bên trong một thẻ. Lưu ý cách chúng ta sử dụng các lớp tiện ích như w-full
, max-w-md
, và grid
trực tiếp trong JSX để kiểm soát bố cục. Đây là sức mạnh của việc kết hợp Shadcn và Tailwind CSS.
Bước 5: Giới Thiệu Các Thành Phần Phức Tạp Hơn
Các trường nhập liệu tĩnh là tốt, nhưng các ứng dụng thực tế cần các yếu tố tương tác. Hãy cải thiện biểu mẫu của chúng ta.
1. Thêm thành phần Select
: Trường nhập "Framework" nên là một dropdown. Hãy thêm thành phần Select
. Thành phần này phức tạp hơn và có dependencies vào các thành phần khác.Bash
npx shadcn-ui@latest add select
CLI đủ thông minh. Nó sẽ thấy rằng Select
yêu cầu thành phần Popover
để hoạt động và sẽ xin phép bạn cài đặt nó cùng với các dependencies của nó. Đây là một tính năng tuyệt vời giúp bạn không phải theo dõi dependencies thủ công.
2. Tích Hợp Thành Phần Select
: Thay thế trường Input
cho "Framework" trong src/app/page.tsx
bằng thành phần Select
mới.TypeScript
// Thêm các import này ở đầu
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
// ... bên trong CardContent
<div className="grid gap-2">
<Label htmlFor="framework">Framework</Label>
<Select>
<SelectTrigger id="framework">
<SelectValue placeholder="Select a framework" />
</SelectTrigger>
<SelectContent>
<SelectItem value="nextjs">Next.js</SelectItem>
<SelectItem value="sveltekit">SvelteKit</SelectItem>
<SelectItem value="astro">Astro</SelectItem>
<SelectItem value="nuxt">Nuxt.js</SelectItem>
</SelectContent>
</Select>
</div>
Làm mới trình duyệt của bạn. Bây giờ bạn có một dropdown chọn hoàn toàn chức năng và dễ tiếp cận, đầy đủ các hiệu ứng động và điều hướng bàn phím phù hợp, tất cả nhờ Radix UI hoạt động ngầm.
3. Thêm Phản Hồi Người Dùng với Toast
: Điều gì xảy ra khi người dùng nhấp vào "Deploy"? Chúng ta nên cung cấp cho họ một số phản hồi. Thành phần Toast
là hoàn hảo cho việc này.
Đầu tiên, thêm nó:Bash
npx shadcn-ui@latest add toast
Tiếp theo, để sử dụng toasts, bạn cần thêm thành phần <Toaster />
vào layout gốc của bạn để nó có thể hiển thị ở bất kỳ đâu trong ứng dụng. Mở src/app/layout.tsx
và sửa đổi nó:TypeScript
import { Toaster } from "@/components/ui/toaster" // Import Toaster
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Toaster /> {/* Thêm nó ở đây, ngay trước khi đóng body */}
</body>
</html>
)
}
Bây giờ, chúng ta cần một cách để kích hoạt toast. Chúng ta sẽ sử dụng hook useToast
. Hãy cập nhật src/app/page.tsx
để biến nó thành một client component và xử lý sự kiện nhấp nút.TypeScript
'use client'; // <-- Thêm dòng này ở đầu tệp
// ... các import khác
import { useToast } from "@/components/ui/use-toast";
export default function Home() {
const { toast } = useToast(); // Lấy hàm toast từ hook
function handleDeploy() {
toast({
title: "Deployment Scheduled!",
description: "Your project 'Name of your project' is being deployed.",
duration: 5000,
});
}
return (
<main className="flex min-h-screen items-center justify-center bg-background p-8">
<Card className="w-full max-w-md">
{/* ... CardHeader và CardContent ... */}
<CardFooter>
<Button className="w-full" onClick={handleDeploy}> {/* Thêm onClick handler */}
Deploy
</Button>
</CardFooter>
</Card>
</main>
);
}
Bây giờ, khi bạn nhấp vào nút "Deploy", một thông báo đẹp mắt sẽ xuất hiện ở góc màn hình của bạn.
Xây Dựng Một Biểu Mẫu Chuyên Nghiệp Với Xác Thực
Hầu hết các ứng dụng thực tế yêu cầu xử lý biểu mẫu mạnh mẽ, bao gồm xác thực phía client. Cách chính thức để xử lý điều này với Shadcn UI là kết hợp nó với react-hook-form
để quản lý trạng thái và zod
để xác thực schema. Hãy xây dựng nó.
Bước 6: Cài Đặt Các Dependencies Cho Biểu Mẫu
Đầu tiên, hãy cài đặt các thư viện cần thiết:Bash
npm install react-hook-form zod @hookform/resolvers
react-hook-form
: Một thư viện biểu mẫu hiệu quả, linh hoạt và có thể mở rộng.zod
: Một thư viện khai báo và xác thực schema ưu tiên TypeScript.@hookform/resolvers
: Một thư viện cầu nối cho phépreact-hook-form
sử dụngzod
để xác thực.
Bước 7: Thêm Thành Phần Form
Của Shadcn
Shadcn UI cung cấp một thành phần Form
đặc biệt hoạt động như một trình bao bọc để kết nối liền mạch react-hook-form
với các thành phần UI của bạn.Bash
npx shadcn-ui@latest add form
Điều này sẽ thêm src/components/ui/form.tsx
. Tệp này cung cấp một tập hợp các thành phần nhận biết ngữ cảnh (Form
, FormField
, FormItem
, FormLabel
, FormControl
, FormDescription
, FormMessage
) giúp giảm đáng kể mã boilerplate.
Bước 8: Tạo Schema Xác Thực
Trong tệp src/app/page.tsx
của bạn, hãy định nghĩa hình dạng và quy tắc của dữ liệu biểu mẫu bằng cách sử dụng zod
.TypeScript
// Thêm các import này ở đầu
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
Bây giờ, hãy tạo schema ngay phía trên thành phần Home
của chúng ta:TypeScript
const formSchema = z.object({
projectName: z.string().min(2, {
message: "Tên dự án phải có ít nhất 2 ký tự.",
}).max(50, {
message: "Tên dự án không được vượt quá 50 ký tự.",
}),
framework: z.string({
required_error: "Vui lòng chọn một framework để hiển thị.",
}),
});
Schema này định nghĩa hai trường: projectName
phải là một chuỗi từ 2 đến 50 ký tự, và framework
là một chuỗi bắt buộc.
Bước 9: Kết Nối Biểu Mẫu
Bây giờ, hãy tái cấu trúc thành phần Home
của chúng ta để sử dụng tất cả các công cụ mới này.TypeScript
export default function Home() {
const { toast } = useToast();
// 1. Định nghĩa biểu mẫu của bạn.
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
projectName: "",
},
});
// 2. Định nghĩa một trình xử lý gửi biểu mẫu (submit handler).
function onSubmit(values: z.infer<typeof formSchema>) {
// Làm gì đó với các giá trị biểu mẫu.
// ✅ Điều này sẽ an toàn về kiểu và đã được xác thực.
console.log(values);
toast({
title: "Bạn đã gửi các giá trị sau:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(values, null, 2)}</code>
</pre>
),
});
}
// 3. Xây dựng JSX với các thành phần Form của Shadcn
return (
<main className="flex min-h-screen items-center justify-center bg-background p-8">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-2xl">Create Project</CardTitle>
<CardDescription>
Deploy your new project in one-click.
</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="projectName"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Name of your project" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage /> {/* Hiển thị lỗi xác thực */}
</FormItem>
)}
/>
<FormField
control={form.control}
name="framework"
render={({ field }) => (
<FormItem>
<FormLabel>Framework</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a framework" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="nextjs">Next.js</SelectItem>
<SelectItem value="sveltekit">SvelteKit</SelectItem>
<SelectItem value="astro">Astro</SelectItem>
<SelectItem value="nuxt">Nuxt.js</SelectItem>
</SelectContent>
</Select>
<FormDescription>
The framework you want to deploy.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">Deploy</Button>
</form>
</Form>
</CardContent>
</Card>
</main>
);
}
Đây là một đoạn mã khá dài, nhưng nó là một mẫu cực kỳ mạnh mẽ và có khả năng mở rộng. Thành phần FormField
xử lý tất cả các kết nối trạng thái, và FormMessage
tự động hiển thị lỗi xác thực chính xác từ schema zod
của bạn khi người dùng tương tác với trường. Hãy thử gửi biểu mẫu với tên dự án trống để xem xác thực hoạt động.
Nắm Vững Việc Tạo Chủ Đề và Tùy Chỉnh
Sức mạnh thực sự của Shadcn UI được giải phóng khi bạn bắt đầu biến nó thành của riêng mình.
Bước 10: Tạo Chủ Đề Nâng Cao với Các Biến CSS
Toàn bộ chủ đề của bạn được định nghĩa bởi các biến CSS trong src/app/globals.css
. Mở tệp này và tìm các khối :root
và .dark
.CSS
/* Ví dụ từ globals.css */
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
/* ... và nhiều hơn nữa */
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
/* ... */
}
- Thay Đổi Màu Sắc: Các giá trị được biểu diễn dưới dạng giá trị HSL (Hue, Saturation, Lightness) mà không có trình bao bọc
hsl()
. Đây là một lựa chọn có chủ ý để dễ dàng thao tác hơn. Để thay đổi màu thương hiệu chính của bạn, bạn chỉ cần tìm các giá trị HSL cho màu của mình và cập nhật các biến--primary
và--primary-foreground
. Trang Shadcn UI Themes có một trình tạo tuyệt vời cho phép bạn chọn màu và sao chép toàn bộ khối chủ đề. - Thay Đổi Bán Kính Bo Tròn: Muốn các góc sắc nét hơn? Thay đổi
--radius: 0.5rem;
thành--radius: 0.2rem;
hoặc thậm chí0rem
. Mọi thành phần có góc bo tròn đều sử dụng biến này, vì vậy thay đổi của bạn sẽ lan truyền toàn cầu.
Triển khai Chế Độ Tối:
Shadcn được cấu hình sẵn cho chế độ tối nhờ khối lớp .dark và chiến lược darkMode: "class" của Tailwind trong tailwind.config.ts. Tất cả những gì bạn cần là một cách để bật/tắt lớp dark trên phần tử <html>. Một thư viện phổ biến cho việc này là next-themes.
- Cài đặt nó:
npm install next-themes
- Tạo một thành phần
ThemeProvider
(src/components/theme-provider.tsx
): TypeScript
"use client"
import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
- Bao bọc
RootLayout
của bạn trong provider này (src/app/layout.tsx
): TypeScript
import { ThemeProvider } from "@/components/theme-provider"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
<Toaster />
</ThemeProvider>
</body>
</html>
)
}
- Cuối cùng, tạo một nút chuyển đổi (ví dụ:
src/components/mode-toggle.tsx
): TypeScript
"use client"
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
export function ModeToggle() {
const { theme, setTheme } = useTheme()
return (
<Button
variant="outline"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
)
}
Bây giờ bạn có thể đặt <ModeToggle />
này ở bất kỳ đâu trong ứng dụng của mình để có một nút chuyển đổi chế độ tối nhận biết hệ thống và có thể ghi đè bởi người dùng.
Bước 11: Tùy Chỉnh Mã Nguồn Thành Phần
Đây là siêu năng lực tối thượng. Giả sử bạn muốn một biến thể thành công (success variant) mới cho nút của mình có nền màu xanh lá cây.
Mở src/components/ui/button.tsx. Tìm định nghĩa buttonVariants. Nó sử dụng cva (Class Variance Authority). Đơn giản chỉ cần thêm một biến thể mới:TypeScript
const buttonVariants = cva(
// ... kiểu dáng cơ bản
{
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 bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
success: "bg-green-600 text-white hover:bg-green-600/90", // Biến thể mới của chúng ta
},
// ... biến thể kích thước
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
Chỉ vậy thôi. Bây giờ bạn có thể sử dụng nó trong mã của mình: <Button variant="success">Success</Button>
. Bạn không cần phải viết các ghi đè CSS phức tạp. Bạn chỉ cần chỉnh sửa mã nguồn của chính thành phần đó. Quy trình làm việc này đơn giản, dễ đoán và cực kỳ mạnh mẽ.
Phần 6: Các Thực Hành Tốt Nhất và Con Đường Phía Trước
Khi ứng dụng của bạn phát triển, đây là một số thực hành tốt nhất cần ghi nhớ.
- Tổ Chức Tệp: Mặc dù CLI đặt mọi thứ vào
components/ui
, thư mục này nên được coi là bộ công cụ UI "cơ sở" của bạn. Đối với các thành phần phức tạp hơn mà bạn tự kết hợp (ví dụ: mộtUserProfileCard
sử dụngCard
,Avatar
vàButton
của Shadcn), hãy tạo chúng trong một thư mục khác, nhưcomponents/shared
hoặccomponents/features
. Điều này giữ sự phân tách rõ ràng giữa UI nền tảng và các thành phần dành riêng cho ứng dụng. - Cập Nhật Thành Phần: Làm thế nào để bạn nhận được các bản cập nhật nếu thành phần Shadcn UI gốc được cải thiện? CLI đã xử lý điều này. Bạn có thể chạy lại
npx shadcn-ui@latest add button
. CLI sẽ phát hiện rằng bạn đã có tệpbutton.tsx
và hiển thị so sánhdiff
, cho phép bạn ghi đè tệp của mình hoặc chấp nhận các thay đổi thủ công. Nó giống như một hệ thống kiểm soát phiên bản nhỏ cho các thành phần của bạn. - Tận Dụng Khả Năng Tiếp Cận (Accessibility): Hãy nhớ rằng các thành phần Shadcn có khả năng tiếp cận ngay từ đầu vì chúng được xây dựng trên các primitive của Radix. Khi bạn tùy chỉnh chúng, hãy lưu ý không làm hỏng khả năng tiếp cận này. Ví dụ, nếu bạn thay đổi màu sắc của một nút, đảm bảo văn bản vẫn có đủ độ tương phản. Khi bạn xây dựng các thành phần mới, hãy cố gắng tuân theo các mẫu được thiết lập bởi Shadcn/Radix để duy trì khả năng điều hướng bằng bàn phím và hỗ trợ trình đọc màn hình.
Kết Luận: Bạn Là Tác Giả Thư Viện
Bây giờ bạn đã đi từ triết lý cốt lõi của Shadcn UI đến việc triển khai các mẫu nâng cao, thực tế. Bạn đã thấy rằng sự đổi mới thực sự của nó không chỉ nằm ở bản thân các thành phần, mà còn ở sự thay đổi mô hình mà nó đại diện. Nó chuyển các nhà phát triển từ việc chỉ là người tiêu dùng của một thư viện sang trở thành người quản lý và người sở hữu bộ công cụ UI của riêng họ.
Bằng cách cung cấp cho bạn mã nguồn thô, xây dựng trên nền tảng vững chắc của Tailwind CSS và Radix UI, và cung cấp trải nghiệm CLI liền mạch, Shadcn UI đạt được sự cân bằng hoàn hảo giữa tốc độ phát triển ban đầu và khả năng bảo trì lâu dài cùng sự tự do sáng tạo. Bạn không còn bị ràng buộc bởi hệ thống thiết kế của người khác. Các thành phần trong dự án của bạn là của riêng bạn—để sửa đổi, mở rộng và hoàn thiện.
Tương lai của UI ứng dụng của bạn không còn nằm trong tay một dependency bên thứ ba; nó nằm ngay trong thư mục components
của bạn. Chúc bạn xây dựng vui vẻ.
Bạn muốn một nền tảng tích hợp, Tất cả trong một để Nhóm Phát triển của bạn làm việc cùng nhau với năng suất tối đa?
Apidog đáp ứng mọi yêu cầu của bạn và thay thế Postman với mức giá phải chăng hơn nhiều!