Phân tích
9 phút đọc2 tháng 6, 20262

Tailwind CSS v4.0 Viết lại từ đầu bằng Rust và mọi thứ thay đổi

Tailwind CSS v4.0 ra mắt với bộ compiler viết bằng Rust, nhanh gấp 10 lần, bỏ file config JS, chuyển sang CSS-first. Mình phân tích chi tiết những gì thay đổi.

N

Nguyễn Nhật Long

@nguyennhatlong1303

Tailwind CSS v4.0 Viết lại từ đầu bằng Rust và mọi thứ thay đổi

Mình vẫn nhớ lần đầu tiên dùng Tailwind CSS khoảng năm 2020, lúc đó team mình còn tranh cãi nhau kịch liệt một nửa bảo "viết class dài loằng ngoằng thế kia thì maintain kiểu gì", nửa còn lại thì thấy nó productive đến mức không muốn quay lại viết CSS thuần nữa. Cuối cùng thì Tailwind thắng, và từ đó đến giờ gần như mọi dự án frontend mình tham gia đều dùng nó. Nên khi v4.0 chính thức release, mình khá hào hứng ngồi đọc changelog và thử nghiệm. Và phải nói thật đây không phải một bản update bình thường. Họ viết lại gần như toàn bộ.

Compiler mới viết bằng Rust nhanh đến mức nào?

Điểm lớn nhất của v4.0 là bộ compiler được viết lại hoàn toàn bằng Rust, thay vì JavaScript như trước. Nếu bạn đã từng làm việc với các dự án lớn dùng Tailwind v3, bạn sẽ biết cái cảm giác chờ build CSS lần đầu khá... chậm. Đặc biệt khi dự án có hàng trăm component, mỗi lần dev server restart là phải scan lại toàn bộ file để tìm class name.

Team Tailwind công bố con số benchmark khá ấn tượng:

Con số 10x nghe có vẻ marketing, nhưng mình đã thử migrate một dự án Next.js cỡ trung bình (~150 component) và cảm nhận thực tế là đúng. HMR giờ gần như instant bạn save file, CSS update ngay lập tức, không còn cái delay nhỏ nhỏ khó chịu nữa. Với dự án nhỏ thì có thể bạn không thấy khác biệt nhiều, nhưng dự án càng lớn thì gap càng rõ.

MetricTailwind v3Tailwind v4Cải thiện
Full build~300ms+~30ms~10x nhanh hơn
Incremental build (HMR)~20-50ms~1-5ms~10x nhanh hơn
Khởi động lần đầuChậm đáng kể với dự án lớnGần như instantCảm nhận rõ rệt

Chuyện viết lại bằng Rust này nằm trong một trend lớn hơn của hệ sinh thái JavaScript. SWC thay thế Babel, esbuild thay thế webpack, Turbopack đang dần thay thế webpack trong Next.js tất cả đều đi theo hướng viết tooling bằng ngôn ngữ compiled (Rust hoặc Go) để giải quyết bottleneck performance. Tailwind đi theo hướng này là hoàn toàn hợp lý.

Bỏ tailwind.config.js chuyển sang CSS-first configuration

Đây là thay đổi mà mình thấy controversial nhất, và cũng là thay đổi mình thích nhất.

Ở v3, bạn cần một file tailwind.config.js (hoặc .ts) để customize theme, extend colors, thêm plugin, config content paths, v.v. File này theo thời gian có thể phình ra khá lớn. Ở v4, toàn bộ configuration chuyển vào trong CSS luôn, dùng directive @theme.

Thay vì viết thế này trong tailwind.config.js:

JavaScript
1// tailwind.config.js (v3)
2module.exports = {
3 content: ['./src/**/*.{js,jsx,ts,tsx}'],
4 theme: {
5 extend: {
6 colors: {
7 brand: '#3b82f6',
8 'brand-dark': '#1d4ed8',
9 },
10 fontFamily: {
11 sans: ['Inter', 'sans-serif'],
12 },
13 },
14 },
15 plugins: [],
16}

Bạn viết trực tiếp trong CSS:

CSS
1/* app.css (v4) */
2@import "tailwindcss";
3
4@theme {
5 --color-brand: #3b82f6;
6 --color-brand-dark: #1d4ed8;
7 --font-sans: 'Inter', sans-serif;
8}

Đơn giản hơn rất nhiều đúng không? Và điều hay ho là mọi thứ bạn define trong @theme đều tự động trở thành CSS custom properties (CSS variables), nghĩa là bạn có thể access chúng ở bất cứ đâu trong CSS mà không cần dùng utility class.

Theo kinh nghiệm của mình, cách tiếp cận CSS-first này có vài lợi ích rõ ràng:

  • Không cần restart dev server khi thay đổi config. Vì config giờ nằm trong CSS, nó được xử lý như một file CSS bình thường HMR hoạt động ngay.
  • Không cần học thêm API riêng của Tailwind config. Bạn chỉ cần biết CSS custom properties.
  • Dễ share configuration giữa các dự án chỉ cần import một file CSS.

Một điểm nữa là v4 tự động detect content files mà không cần bạn config content array nữa. Nó sử dụng heuristic thông minh để scan các file trong project, bỏ qua node_modules, .git, và các thư mục binary. Nếu bạn cần override thì vẫn có thể dùng @source directive, nhưng phần lớn trường hợp bạn không cần đụng đến.

CSS hiện đại không còn hack workaround

Tailwind v4 embrace CSS hiện đại một cách toàn diện. Điều này có nghĩa là nó sử dụng các tính năng CSS native thay vì phải workaround bằng JavaScript hay PostCSS transforms.

Cascade layers (@layer): Tailwind v4 sử dụng native CSS @layer thay vì simulate nó. Điều này giúp quản lý specificity tốt hơn nhiều. Nếu bạn từng gặp bug kiểu "tại sao class custom của mình bị Tailwind utility override" thì v4 xử lý chuyện này sạch sẽ hơn hẳn.

color-mix() cho opacity: Thay vì generate hàng đống CSS cho mỗi opacity variant của mỗi màu, v4 dùng color-mix() một CSS function được hỗ trợ rộng rãi trên các browser hiện đại. Kết quả là output CSS nhỏ hơn đáng kể.

Container queries built-in: Ở v3 bạn cần plugin @tailwindcss/container-queries. Ở v4, nó là first-class citizen. Bạn dùng @container và các utility như @sm:, @md: trực tiếp mà không cần cài thêm gì.

@starting-style và anchor positioning: Đây là những CSS feature khá mới, và Tailwind v4 đã có utility classes cho chúng. Mình thấy cái này hay ở chỗ nó cho phép bạn tạo animation cho element khi nó xuất hiện lần đầu (ví dụ popover, dialog) mà không cần JavaScript.

Featurev3v4
Cascade layersSimulated qua PostCSSNative CSS `@layer`
Color opacityGenerate nhiều CSS rules`color-mix()` native
Container queriesCần plugin riêngBuilt-in
`@starting-style`Không hỗ trợCó utility classes
`@property` registrationKhôngCó, cho animated gradients
`light-dark()`KhôngHỗ trợ native

Những utility mới đáng chú ý

Ngoài việc thay đổi kiến trúc, v4 cũng bổ sung khá nhiều utility class mới. Mình liệt kê mấy cái mình thấy sẽ dùng nhiều nhất:

inset-shadow-*inset-ring-*: Giờ bạn có thể compose nhiều loại shadow lên cùng một element. Ví dụ một button có cả shadow-md, inset-shadow-sm, và ring-1 cùng lúc mà không bị conflict. Trước đây làm chuyện này phải viết custom CSS.

HTML
1<button class="shadow-md inset-shadow-sm inset-shadow-white/20 ring-1 ring-blue-600">
2 Click me
3</button>

text-shadow-*: Cuối cùng cũng có text shadow utility! Mình không hiểu sao v3 không có cái này, phải custom config hoài.

not-* variant: Cho phép bạn apply style khi một condition KHÔNG thỏa mãn. Ví dụ not-last:border-b thêm border bottom cho tất cả item trừ item cuối. Cái này cực kỳ tiện cho list rendering.

nth-* variant: nth-3:bg-gray-100 apply style cho item thứ 3. Hoặc nth-odd:bg-gray-50 cho zebra striping. Trước đây phải dùng odd:even: riêng, giờ có thêm nth-* linh hoạt hơn nhiều.

Field sizing: field-sizing-content cho phép textarea tự động expand theo content mà không cần JavaScript. Đây là một CSS feature mới và Tailwind wrap nó lại rất gọn.

Migrate từ v3 lên v4 có đau không?

Câu trả lời ngắn: có tool hỗ trợ, nhưng vẫn cần review kỹ.

Team Tailwind cung cấp một CLI migration tool:

Terminal
1npx @tailwindcss/upgrade

Tool này sẽ tự động:

  • Chuyển tailwind.config.js sang @theme trong CSS
  • Update các class name đã bị rename (ví dụ shadow-sm giờ là shadow-xs, shadow giờ là shadow-sm yeah, họ shift hết xuống một bậc)
  • Update PostCSS config nếu cần
  • Chuyển @apply và các directive sang syntax mới

Tuy nhiên, anh em lưu ý mấy điểm:

  1. Shadow scale bị shift: Đây là breaking change dễ miss nhất. shadow-sm cũ ≠ shadow-sm mới. Tool migration sẽ handle, nhưng nếu bạn có hardcode shadow value ở đâu đó ngoài Tailwind class thì phải check lại.
  2. @apply trong component CSS: Nếu bạn dùng @apply nhiều (mình biết nhiều team dùng), hầu hết vẫn hoạt động, nhưng có một số edge case với @apply trong file CSS được import từ node_modules có thể gặp vấn đề.
  3. Browser support: Vì v4 dùng nhiều CSS modern features, nó yêu cầu browser target cao hơn. Nếu bạn vẫn cần support IE hoặc các browser cũ, thì... đừng upgrade vội. Nhưng thực tế năm 2025 rồi, hầu hết dự án không còn cần lo chuyện này.
  4. PostCSS plugin thay đổi: Nếu bạn dùng Tailwind như PostCSS plugin, package name đổi thành @tailwindcss/postcss. Nếu dùng Vite thì có @tailwindcss/vite và mình recommend dùng Vite plugin vì nó nhanh hơn đi qua PostCSS.

Vite-first, nhưng không bắt buộc

Một thay đổi quan trọng về mặt integration: Tailwind v4 được thiết kế để hoạt động tốt nhất với Vite. Có một Vite plugin chuyên dụng @tailwindcss/vite cho performance tối ưu. Nhưng nếu bạn dùng webpack, Turbopack, hay bất cứ bundler nào khác, PostCSS plugin vẫn hoạt động bình thường.

JavaScript
1// vite.config.ts
2import tailwindcss from '@tailwindcss/vite'
3
4export default {
5 plugins: [
6 tailwindcss(),
7 ],
8}

Với Next.js, hiện tại bạn vẫn dùng PostCSS plugin. Nhưng mình đoán khi Next.js chuyển hoàn toàn sang Turbopack thì sẽ có integration riêng.

Mình nghĩ gì sau khi dùng thử?

Sau khoảng 2 tuần dùng v4 cho một side project, mình có vài nhận xét:

Cái mình thích nhất không phải tốc độ build (dù nó rất tốt) mà là việc bỏ file config JS. Nghe nhỏ nhưng nó thay đổi mental model khá nhiều. Mọi thứ liên quan đến styling giờ nằm trong CSS, đúng chỗ của nó. Không còn chuyện nhảy qua nhảy lại giữa config file và CSS file.

Cái mình chưa quen là shadow scale mới. Muscle memory cứ gõ shadow-md nhưng giờ nó to hơn trước một bậc, phải điều chỉnh lại. Chuyện nhỏ nhưng hơi annoying trong tuần đầu.

Một điều nữa là ecosystem plugin chưa catch up hết. Một số plugin third-party viết cho v3 chưa tương thích v4. Nếu bạn depend nhiều vào plugin bên ngoài, check kỹ trước khi upgrade.

Nhìn chung, mình nghĩ đây là bản update đáng giá nhất của Tailwind kể từ khi v2 introduce JIT mode. Nếu bạn đang bắt đầu dự án mới, không có lý do gì không dùng v4. Còn dự án đang chạy v3 ổn định thì cứ từ từ, chờ ecosystem ổn định thêm rồi migrate cũng không muộn. Cái migration tool chạy khá ngon, nhưng với production app thì cẩn thận vẫn hơn.

Cuối cùng, nếu bạn chưa từng dùng Tailwind và đang cân nhắc, thì v4 là thời điểm tốt nhất để bắt đầu. Setup đơn giản hơn bao giờ hết chỉ cần @import "tailwindcss" trong file CSS là xong, không config, không phức tạp. Cứ thử đi rồi biết.

NN

Nguyễn Nhật Long

@nguyennhatlong1303

Nguyễn Nhật Long is a Senior Frontend Engineer and Frontend Team Leader with 7 years of experience building real-time fintech platforms. Specializing in React, Next.js, TypeScript, and React Native, shipping 10+ products across Web, Mobile, Telegram Mini-Apps, and Web3.

Thấy hay? Chia sẻ cho bạn bè!