Next.js x Panda CSS: Tạm biệt “Class Name cực hình” với Type-safe CSS-in-JS

Development tutorial - IT technology blog
Development tutorial - IT technology blog

Tại sao phải đổi Tailwind lấy Panda CSS?

Nếu bạn từng code Tailwind trong các dự án Next.js lớn, chắc hẳn bạn đã nếm trải cảm giác “hoa mắt” vì những dòng class dài dằng dặc. Đôi khi một cái div gánh tới 20-30 utility classes khiến code HTML trở nên cực kỳ khó đọc. Ngược lại, các thư viện CSS-in-JS đời đầu như Styled-components hay Emotion lại làm nặng main thread và thường xuyên xung đột với cơ chế React Server Components (RSC).

Panda CSS xuất hiện như một vị cứu tinh. Nó cho phép bạn viết style kiểu object giống Styled-components nhưng lại trích xuất ra file CSS tĩnh ngay lúc build. Qua thực tế triển khai, mình nhận thấy khả năng gợi ý (Intellisense) của Panda mạnh hơn hẳn Tailwind. Bạn sẽ không còn lo gõ nhầm text-gry-500 thay vì text-gray-500 vì TypeScript sẽ báo lỗi ngay lập tức.

Cơ chế Zero-runtime: Bí mật đằng sau tốc độ

Panda hoạt động theo cơ chế Static Analysis thay vì tính toán style khi ứng dụng đang chạy. Nó quét toàn bộ source code, nhận diện các hàm style và gom tất cả vào một file CSS duy nhất trước khi deploy.

Cách tiếp cận này mang lại ba lợi ích sát sườn:

  • Zero-runtime: Trình duyệt không tốn thêm milisecond nào để xử lý JavaScript cho CSS. Tốc độ load trang sẽ nhanh hơn đáng kể.
  • Type-safety tuyệt đối: Mọi token từ màu sắc đến khoảng cách đều được định nghĩa chặt chẽ. Nếu bạn dùng một mã màu không tồn tại trong Design System, code sẽ không thể compile.
  • Thân thiện với RSC: Vì style được trích xuất tĩnh, bạn có thể thoải mái dùng Panda trong Server Components mà không cần khai báo "use client" ở đầu file.

Các bước tích hợp vào dự án Next.js

Hãy cùng mình setup nhanh một project Next.js sử dụng App Router từ đầu nhé.

Bước 1: Khởi tạo dự án

Chạy lệnh sau để tạo project mới:

npx create-next-app@latest my-panda-project --typescript --tailwind --eslint
cd my-panda-project

Mặc dù chúng ta dùng Panda, việc giữ Tailwind giai đoạn đầu có thể giúp bạn so sánh hoặc tận dụng các plugin có sẵn nếu cần.

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

Cài đặt thư viện và khởi tạo file cấu hình:

npm install -D @pandacss/dev
npx panda init

Sau lệnh này, file panda.config.ts sẽ xuất hiện. Đây là “đầu não” để bạn định nghĩa theme, breakpoint và các quy tắc quét file.

Bước 3: Cấu hình PostCSS

Mở file postcss.config.mjs và thêm plugin của Panda vào:

export default {
  plugins: {
    '@pandacss/dev/postcss': {},
  },
};

Bước 4: Tự động hóa quá trình Gen Code

Panda cần tạo ra thư mục styled-system để chứa các định nghĩa kiểu. Hãy cập nhật package.json để mọi thứ luôn đồng bộ:

{
  "scripts": {
    "prepare": "panda codegen",
    "dev": "next dev",
    "build": "panda codegen && next build"
  }
}

Lưu ý: Thư mục styled-system chứa rất nhiều file tự động tạo ra. Bạn nên thêm nó vào .gitignore để tránh làm rối repository.

Bắt tay vào viết Style

Thay vì viết chuỗi class, bạn sẽ sử dụng hàm css cực kỳ trực quan:

import { css } from '../styled-system/css';

export default function Home() {
  return (
    <div className={css({ 
      fontSize: "2xl", 
      fontWeight: 'bold', 
      color: 'blue.600', 
      _hover: { color: 'red.500' } 
    })}>
      Chào anh em, đây là Panda CSS!
    </div>
  );
}

Cú pháp _hover hay _dark giúp code trông gọn gàng hơn nhiều so với tiền tố của Tailwind. Khi làm việc với các hệ thống Design System phức tạp, mình thường dùng JSON Formatter của Toolcraft để kiểm tra và format lại các object theme trước khi đưa vào config. Việc này giúp hạn chế lỗi cú pháp rất hiệu quả so với việc gõ tay thủ công.

Tái sử dụng với Recipes

Recipes là cách Panda giúp bạn tạo ra các component có nhiều biến thể (variants). Hãy tưởng tượng bạn cần một Button có các size sm, md, lg và các style primary, outline.

Định nghĩa trong panda.config.ts:

recipes: {
  button: {
    className: 'button',
    base: { padding: '2', borderRadius: 'md' },
    variants: {
      visual: {
        solid: { bg: 'blue.500', color: 'white' },
        outline: { border: '1px solid', borderColor: 'blue.500' }
      }
    }
  }
}

Khi sử dụng, bạn chỉ cần gọi: className={button({ visual: 'solid' })}. Rất sạch sẽ!

Kinh nghiệm thực chiến

Sau vài dự án thực tế, mình rút ra 3 kinh nghiệm xương máu:

  1. Path Alias: Hãy cấu hình alias @/styled-system trong tsconfig.json. Nó giúp bạn import style ở bất cứ đâu mà không cần quan tâm đến độ sâu của thư mục.
  2. Kiểm soát Bundle Size: Đừng lạm dụng quá nhiều variants phức tạp cho những component ít dùng. File CSS tĩnh có thể phình to nếu bạn tạo ra hàng nghìn tổ hợp style không cần thiết.
  3. Tận dụng Shorthand: Bạn có thể cấu hình mt thay cho marginTop trong config để gõ code nhanh như Tailwind nhưng vẫn có Type-safe.

Lời kết

Panda CSS không chỉ là một công cụ, nó là cách tư duy mới về việc quản lý giao diện. Nó kết hợp được sự linh hoạt của CSS-in-JS và hiệu suất đỉnh cao của CSS tĩnh. Nếu bạn đang bắt đầu một dự án Next.js quy mô lớn, hãy thử đưa Panda vào stack của mình. Chắc chắn trải nghiệm dev của bạn sẽ sang một trang mới!

Bạn có gặp khó khăn gì khi setup không? Đừng ngần ngại để lại comment phía dưới, mình sẽ cùng giải đáp nhé!

Share: