Hướng dẫn tạo module tùy chỉnh với shadcn: Từ A đến Z

2026-05-19 · Uncategorized · 읽는데 21분

Hướng dẫn tạo module tùy chỉnh với shadcn: Từ A đến Z

썸네일

Shadcn là gì mà khiến dân dev xôn xao?

Hồi đầu năm 2023, mình còn nhớ như in cái cảm giác "chán ngán" khi mở dự án mới và biết rằng sắp phải đối mặt với một đống UI library nặng nề, khó tùy chỉnh. Rồi bỗng dưng shadcn xuất hiện như một làn gió mới.

Không phải là thư viện, cũng chẳng phải framework – shadcn tự nhận mình là "tập hợp các component có thể tái sử dụng mà bạn copy-paste vào dự án". Nghe có vẻ lạ đúng không?

Một thư viện UI mà không cài qua npm? Đó chính là điểm khác biệt làm nên tên tuổi của shadcn.

Hãy nhìn vào con số: chỉ sau 19 tháng, repo shadcn đã đạt gần 70.000 stars. So với Material UI (khoảng 92.000 stars sau nhiều năm), tốc độ tăng trưởng này thực sự ấn tượng.

Vậy điều gì đã tạo nên sức hút đó?

Đặc điểm Thư viện truyền thống (Material UI) Shadcn
Cách cài đặt npm install Copy-paste hoặc CLI
Kích thước project Nặng, nhiều component không dùng Chỉ lấy component cần
Tùy chỉnh Phức tạp, override CSS Dễ dàng, sửa trực tiếp code
Phụ thuộc Nhiều dependency Tối thiểu

Cái hay của shadcn nằm ở triết lý "headless" kết hợp với Tailwind CSS. Nó dùng Radix UI – một thư viện headless tập trung vào accessibility – làm nền tảng, rồi "khoác áo" cho các component bằng Tailwind.

Kết quả? Bạn có component đẹp, chuẩn accessibility, và quan trọng nhất: toàn quyền kiểm soát code.

Sau khi hiểu shadcn là gì, chắc bạn cũng tò mò: làm sao để xây dựng module tùy chỉnh với nó? Hãy cùng mình đi vào phần thực chiến.

Chuẩn bị môi trường Đừng để "bài vở" làm khó bạn

Trước khi bắt tay vào code, mình muốn kể một câu chuyện nhỏ. Có lần mình mất gần 2 tiếng để config shadcn với Docusaurus, chỉ vì không đọc kỹ tài liệu.

Đúng là "dốt đặc còn hơn thông minh" mà không chịu học hỏi. Thực tế, việc thiết lập môi trường cho shadcn khá đơn giản nếu bạn làm đúng trình tự.

Dưới đây là các bước mình đã rút ra sau nhiều lần "vấp ngã":

Bước 1: Khởi tạo dự án React/Vite/Next.js

Mình khuyên bạn dùng Vite cho dự án nhỏ, Next.js cho dự án lớn. Cả hai đều giúp shadcn tốt.

Bước 2: Cài đặt Tailwind CSS

bash npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p

Bước 3: Config shadcn

bash npx shadcn@latest init

Lúc này hệ thống sẽ hỏi bạn vài câu để tạo file components.json. Đừng vội "next" qua, hãy đọc kỹ:

Câu hỏi Gợi ý trả lời
Which style would you like to use? Default (New York)
Which color would you like to use? Neutral hoặc Zinc
Where is your global CSS file? src/index.css
Do you want to use CSS variables? Yes
Where is your tailwind.config.js? tailwind.config.js
Configure the import alias for components? @/components
Configure the import alias for utils? @/lib/utils

Mẹo nhỏ: Nên chọn CSS variables để dễ dàng đổi theme sau này. Nếu bạn chọn "No", shadcn sẽ dùng class-based, khá bất tiện khi muốn chuyển đổi sáng/tối.

Sau khi init xong, bạn sẽ thấy thư mục src/components/ui và src/lib/utils.ts được tạo tự động. Nếu không, hãy kiểm tra lại config.

OK, môi trường đã sẵn sàng. Giờ đến lúc "phiêu" với việc tạo module tùy chỉnh.

Xem thêm #1

Cách tạo module tùy chỉnh Copy-paste hay CLI?

Đây là câu hỏi mà nhiều bạn mới hỏi mình nhất. Câu trả lời: tùy vào tình huống.

Hãy tưởng tượng bạn đang làm việc trong nhóm 5 người, ai cũng cần dùng component Button. Dùng CLI sẽ đồng bộ được version.

Còn nếu bạn solo project, copy-paste nhanh hơn nhiều.

Cách 1 Dùng CLI (Khuyên dùng cho team)

bash npx shadcn@latest add button npx shadcn@latest add card npx shadcn@latest add dialog

Mỗi lần chạy lệnh, shadcn sẽ:

  1. Tải component vào src/components/ui/
  2. Cập nhật dependencies nếu cần
  3. Tích hợp với tailwind.config.js

Mình từng thử add 10 component liên tiếp, mất khoảng 2 phút. So với việc tự viết từ đầu mất 2-3 giờ, đây là con số đáng để suy nghĩ.

Cách 2 Copy-paste (Dành cho dự án đặc thù)

Một số framework như Docusaurus không giúp CLI shadcn. Lúc này bạn phải copy-paste thủ công.

Các bước:

  1. Vào trang https://ui.shadcn.com
  2. Chọn component mong muốn
  3. Click "Copy" code
  4. Tạo file .tsx trong src/components/ui/
  5. Paste code và điều chỉnh import path

Ví dụ với Button component:

tsx // src/components/ui/button.tsx import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils"

Lưu ý: Bạn cần cài thêm class-variance-authority và @radix-ui/react-slot nếu chưa có.

So sánh thời gian thực tế

Phương pháp Thời gian cho 1 component Phù hợp
CLI 10-15 giây Team, dự án React/Next
Copy-paste 2-3 phút Docusaurus, Gatsby
Tự viết từ đầu 2-4 giờ Dự án đặc thù cao

Sau khi có component, bước tiếp theo là tùy chỉnh chúng. Đây mới là phần "chất" nhất.

Tùy chỉnh component Khi "chuẩn" chưa đủ, cần "độc"

Mình từng gặp một bạn dev than phiền: "Em dùng shadcn mà sao thấy giống hệt mấy trang demo, chán quá". Đúng, nếu bạn chỉ dùng nguyên bản, ai cũng giống ai.

Nhưng sức mạnh của shadcn nằm ở khả năng tùy chỉnh.

Cấu trúc theme của shadcn

Shadcn dùng CSS variables để quản lý màu sắc. Nhìn vào file globals.css bạn sẽ thấy:

css :root { --background: 0 0% 100%; --foreground: 240 10% 3.9%; --card: 0 0% 100%; --card-foreground: 240 10% 3.9%; --primary: 240 5.9% 10%; --primary-foreground: 0 0% 98%; }

Mỗi biến là một giá trị HSL (Hue, Saturation, Lightness). Để đổi màu chủ đạo, bạn chỉ cần sửa giá trị Hue.

Ví dụ: Muốn theme màu xanh lá, đổi --primary: 142 76% 36% (màu xanh lá chuẩn).

Cách tạo module tùy chỉnh từ component có sẵn

Giả sử bạn muốn tạo một Card có hiệu ứng hover đặc biệt. Đừng sửa trực tiếp file card.tsx – hãy tạo wrapper:

```tsx // src/components/custom/CustomCard.tsx import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"

export function CustomCard({ title, children }) { return ( {title} {children} ) } ```

Cách này giúp bạn giữ nguyên component gốc, dễ dàng update sau này.

Tạo module mới từ Radix + Tailwind

Đôi khi shadcn chưa có component bạn cần. Lúc này hãy tự build dựa trên Radix.

Ví dụ: Tạo một Tooltip component:

```tsx // src/components/ui/tooltip.tsx import * as TooltipPrimitive from "@radix-ui/react-tooltip" import { cn } from "@/lib/utils"

const TooltipProvider = TooltipPrimitive.Provider const Tooltip = TooltipPrimitive.Root const TooltipTrigger = TooltipPrimitive.Trigger

const TooltipContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef

(({ className, sideOffset = 4, ...props }, ref) => ( ))

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } ```

Mất khoảng 15 phút để viết, nhưng bạn đã có một component chuẩn accessibility, style theo theme shadcn.

Bảng tổng hợp cách tùy chỉnh

Mục đích Cách làm Mức độ khó
Đổi màu theme Sửa CSS variables Dễ
Thêm hiệu ứng Dùng Tailwind classes Dễ
Mở rộng component Tạo wrapper Trung bình
Tạo component mới Dùng Radix + Tailwind Khó

Tới đây, mình tin bạn đã có thể tự tin tạo module tùy chỉnh. Nhưng còn một vấn đề: làm sao để quản lý nhiều module mà không rối?

Quản lý và tối ưu module Bí kíp từ thực tế

Sau 10 năm trong nghề, mình đúc kết được vài nguyên tắc vàng khi làm việc với shadcn.

Nguyên tắc 1 Phân loại thư mục rõ ràng

src/ components/ ui/ # Component gốc từ shadcn custom/ # Module tùy chỉnh features/ # Module theo tính năng layouts/ # Layout component

Nguyên tắc 2 Dùng barrel exports

Tạo file index.ts trong mỗi thư mục để export gọn gàng:

ts // src/components/ui/index.ts export { Button } from "./button" export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } from "./card"

Khi import, chỉ cần:

tsx import { Button, Card } from "@/components/ui"

Nguyên tắc 3 Tối ưu bundle size

Shadcn vốn đã nhẹ, nhưng nếu bạn dùng nhiều component từ Radix, hãy chú ý:

Component Kích thước (minified) Khuyên dùng
Button 1.2 KB Luôn dùng
Card 0.8 KB Luôn dùng
Dialog 4.5 KB Khi cần modal
Dropdown Menu 6.2 KB Khi cần menu
Data Table 15 KB Chỉ khi cần bảng phức tạp

Mẹo: Chỉ add component khi thực sự cần, đừng add sẵn "để đó sau".

Nguyên tắc 4 Version control

Khi update shadcn lên version mới, đừng chạy CLI lại toàn bộ. Hãy:

  1. Check changelog xem component nào thay đổi
  2. Chỉ update những component cần thiết
  3. So sánh code cũ và mới trước khi merge

Xem thêm #2

Kết luận Shadcn có đáng để bạn đầu tư?

Nhìn lại hành trình từ lúc shadcn ra mắt đến nay, mình thấy đây không chỉ là một công cụ, mà còn là một cách tiếp cận mới trong thiết kế UI. Thay vì phụ thuộc vào thư viện đóng gói sẵn, bạn lấy quyền kiểm soát vào tay mình.

Con số 70.000 stars không phải là ngẫu nhiên. Nó phản ánh nhu cầu thực sự của cộng đồng: một giải pháp UI vừa nhanh, vừa linh hoạt, vừa không kèm theo "hàng đống code thừa".

Nếu bạn đang phân vân giữa shadcn và các thư viện khác, hãy thử nghiệm với một dự án nhỏ. Mình cá là sau đó bạn sẽ không muốn quay lại cách cũ đâu.

Còn nếu bạn đã dùng shadcn rồi, hãy chia sẻ kinh nghiệm của mình ở phần bình luận. Mình rất muốn học hỏi thêm từ cộng đồng.

Chúc bạn code vui vẻ và đừng quên: "Copy-paste không có gì xấu, miễn là bạn hiểu mình đang copy cái gì".

Video liên quan

Bài viết liên quan