- Mặc dù nút radio được tích hợp sẵn trong trình duyệt web chỉ là một phần tử HTML đơn giản, thư viện UI Shadcn lại tái cấu trúc nó thành nhiều lớp component React
<RadioGroup> và <RadioGroupItem> của Shadcn tiếp tục bọc các component của Radix UI, đồng thời sử dụng biểu tượng lucide-react và hàng chục lớp Tailwind
- Radix tận dụng thuộc tính ARIA để phục vụ khả năng truy cập và tùy biến, nhưng trên thực tế lại tái sử dụng phần tử button thay vì
<input type="radio"> mặc định
- Dù vẫn có thể đạt được cùng kiểu hiển thị chỉ với CSS đơn giản, cấu trúc này lại thêm vào hàng trăm dòng mã và nhiều phụ thuộc, gây ra độ phức tạp không cần thiết
- Việc không tái sử dụng các phần tử HTML gốc làm giảm hiệu năng và tăng gánh nặng bảo trì, đồng thời làm mất đi sự đơn giản vốn có của phát triển web
Phân tích cấu trúc nút radio của Shadcn
- Shadcn triển khai nút radio thông qua hai component
<RadioGroup> và <RadioGroupItem>
- Mỗi component đều bọc các primitive được lấy từ
@radix-ui/react-radio-group, đồng thời dùng CircleIcon của lucide-react
- Bao gồm hơn 45 dòng mã và 3 import bên ngoài, với kiểu dáng được định nghĩa bằng hơn 30 lớp Tailwind
- Cấu trúc này còn tải về cả thư viện biểu tượng SVG chỉ để hiển thị một vòng tròn đơn giản
- Trong khi đó, tính năng này hoàn toàn có thể thay thế bằng
border-radius của CSS hoặc phần tử <circle>
Vai trò của Radix UI
- Radix mà Shadcn sử dụng là thư viện component UI cấp thấp tập trung vào khả năng truy cập và tùy biến
- Phần triển khai radio group của Radix gồm khoảng 215 dòng mã React và import 7 tệp
- Radix thêm thuộc tính ARIA vào phần tử
<button> để nó hoạt động giống như nút radio
- Tuy nhiên, nguyên tắc đầu tiên khi dùng ARIA của W3C nêu rõ: “nếu có thể, hãy dùng phần tử HTML gốc”
- Radix không tuân theo nguyên tắc này mà tái sử dụng button thay vì dùng
<input>
- Chỉ khi ở bên trong
<form> thì cấu trúc này mới thêm một <input type="radio"> ẩn, khiến tính nhất quán bị giảm
Phương án đơn giản có thể làm bằng CSS
- Nút radio HTML gốc có thể được style dễ dàng bằng
appearance: none, ::before, :checked, border-radius...
- Trong mã ví dụ, việc tùy biến hoàn chỉnh được thực hiện không cần phụ thuộc, JavaScript hay thuộc tính ARIA
- Cùng hiệu ứng đó cũng có thể triển khai bằng Tailwind
- Nhận định rằng “style nút radio là khó” là vấn đề của quá khứ; hiện nay CSS thuần đã đủ để kiểm soát đầy đủ
Vấn đề tích lũy của độ phức tạp
- Khi dùng đồng thời Shadcn và Radix, lập trình viên phải hiểu hai thư viện và hàng trăm dòng mã
- Chỉ để làm một nút radio đơn giản mà vẫn phải tải thêm vài KB JavaScript
- Người dùng phải chờ quá trình parse và thực thi JS chỉ để bật/tắt một nút
- Cấu trúc như vậy dẫn đến tăng tải nhận thức, mở rộng khả năng phát sinh lỗi và làm giảm hiệu năng web
Quay lại với sự đơn giản
- Trình duyệt vốn đã cung cấp sẵn nút radio, và chỉ một dòng
<input type="radio" name="beverage" value="coffee" /> là đủ
- Những lớp trừu tượng không cần thiết và việc dùng thư viện lồng nhau làm tổn hại đến sự đơn giản và hiệu quả vốn có của phát triển web
- Ngay cả với những thành phần UI nhỏ, thiết kế tái sử dụng chức năng gốc vẫn có lợi hơn cho cả khả năng bảo trì lẫn hiệu năng
4 bình luận
Nhàm chán và hàn lâm quá mức:
Xong rất nhanh, nhớ rất lâu:
Nhìn component button của react aria chắc xỉu luôn ấy, haha
Vì tôi làm trong mảng frontend nên đây là vấn đề tôi đã gặp suốt một thời gian dài; nói sao nhỉ, đúng là một vấn đề cực kỳ khó giải quyết. Cách triển khai thì liên tục thay đổi theo thời đại, nhưng việc không giải quyết bằng
inputtype thì ở thời nào cũng như nhau... Việc cố bắt chước hành vi của nút radio checkbox trên trình duyệt web rồi lại tự triển khai riêng các spec liên quan đến accessibility thì rốt cuộc là đang định làm gì vậy... Tôi thật sự không hiểu... Như trong bài có nói, giờ ngay cả với CSS cũng đã có phương án thay thế, nên nhìn việc nhất quyết phải triển khai bằng component đến chết thì cũng hơi buồn cười thật.Ý kiến trên Hacker News
Tôi không thường xuyên làm frontend, nhưng từ thời điểm React trở thành xu hướng chủ đạo, đã thấy dấu hiệu độ phức tạp ngày càng tăng
Các lớp trừu tượng khác thường có xu hướng đơn giản hóa, nhưng React lại tạo ra một lớp trừu tượng phức tạp hơn nhiều so với công nghệ nền tảng của nó
Tôi có cảm giác các lập trình viên chỉ biết React cứ liên tục chồng thêm các tầng cao hơn, dẫn đến kết quả bị thiết kế quá mức
Ví dụ, Shadcn hay Radix là các thư viện UI chỉ dành cho React, nhưng nếu chỉ nhìn câu chữ marketing thì lại trông như thư viện UI thông thường
Khi quy mô tăng lên thì cuối cùng либо là tôi tự làm framework riêng, либо là thấy không hài lòng với framework sẵn có, và React phần nào giải quyết được chuyện đó
Theo tôi, vấn đề không nằm ở bản thân React mà là độ phức tạp quá mức của hệ sinh thái. Nếu chỉ dùng React tốt thì vẫn có thể dùng rất thoải mái
Họ chỉ tìm cách lách CSS bằng các công cụ như Tailwind. Tôi dùng React để quản lý state, nhưng vẫn thích tự viết phần style bằng CSS
Việc khó nhất là thuyết phục đồng đội chịu học CSS
Tôi tránh những framework “hiện đại” kiểu này và ưu tiên các công nghệ nền tảng khi có thể
React chỉ cung cấp “cái hộp”, còn cho gì vào trong là do lập trình viên quyết định. Đó mới là sức mạnh thực sự của React
Ví dụ radio button này vừa buồn cười vừa gây ấn tượng
Thành phẩm nhìn chẳng khác gì radio button CSS cơ bản, nên tôi thấy khó hiểu vì sao phải làm phức tạp đến vậy
Tôi tò mò liệu có ví dụ nào về các site quy mô lớn được xây mà không có sự phức tạp thừa thãi như thế này không
Lượng code nhiều hơn bây giờ, nhưng giao diện lại có độ phản hồi tức thì
Có thể tham khảo code dự án Takeoff
Kiểu như câu “không ai bị sa thải vì chọn React”, nó đã trở thành lựa chọn an toàn
Lập trình viên nên nhớ rằng họ luôn có thể phản biện các yêu cầu thiết kế
Tôi từng thấy một lập trình viên React Native lãng phí 4 tiếng vì một vấn đề layout đơn giản; bảo họ thử hỏi xem “có thể đổi thiết kế một chút không”, và chỉ 10 phút sau là xong
Chỉ cần dùng CSS tốt là có thể đạt kết quả đủ tốt. Tôi muốn nghe gợi ý từ những ai đã thử cách tiếp cận này
Sai lầm lớn nhất trong năm 2025 của tôi là chọn Shadcn
Thấy nó cứ liên tục import Radix là cảnh báo đầu tiên, nhìn component radio là cảnh báo thứ hai
Vì dự án đã đi được một đoạn nên tôi đành chịu và sửa bằng Copilot, nhưng rốt cuộc tôi cũng không thích việc phụ thuộc vào AI
Bản POC trước đó đơn giản và hiệu quả hơn nhiều. Có ngày tôi muốn làm lại toàn bộ bằng HTML thuần
Remix hay React Router 7 ít ra vẫn có nỗ lực giữ mọi thứ gần với web standard hơn
Tôi đã thấy Tailwind “không ổn”, và nếu sau khi bạn bè refactor mà họ vẫn bảo tốt thì lúc đó tôi sẽ xem lại
React đã có styling dựa trên props, nên không có lý do gì phải dùng cả đống class CSS
Nếu ưu tiên accessibility thì chỉ dùng Radix UI cũng đã đủ
Vấn đề là phần tử
<input>của trình duyệt, đặc biệt là radio và select, rất khó tùy biếnRadio button mặc định có trải nghiệm sử dụng kém trên di động
Tôi muốn biết cụ thể hơn là trên di động đã gặp vấn đề gì
<label>và thêm padding là trên di động cũng dùng ổnHầu hết dự án đều bắt đầu với ý tốt, nhưng rồi đến lúc nào đó lại đầy những đoạn code radio button dài 200 dòng cùng 7 cái import
Đó là lúc code rot bắt đầu
Gần đây tôi thử daisyUI và khá thích nó
Vì dựa trên CSS thuần nên nó tận dụng tốt các tính năng mới của trình duyệt (như dialog)
Ví dụ Drawer không thể trap focus, còn Accordion thì lạm dụng radio button như một cách thay thế JS
Chính vì vậy mà các thư viện như Radix buộc phải trở nên phức tạp
Tôi đồng cảm với ý chính của bài viết, nhưng nếu muốn tái hiện chính xác style mà designer làm trên Figma ở mọi trình duyệt, thì liệu CSS thuần có làm được không?
Có thể tái tạo hoàn toàn thứ như demo của Radix không?
Có thể xem ví dụ CodePen
Chỉ cần tách CSS ra rồi gắn vào một component React đơn giản là đủ
Có thực sự đáng để thêm hàng chục KB code và gánh nặng bảo trì chỉ để tránh vài khác biệt thị giác nhỏ?
Như câu nói của Nam June Paik, “quá hoàn hảo thì thần sẽ nổi giận”
Chi phí thật sự không phải là code mà là thời gian onboarding
Một lập trình viên mới vào sẽ mất vài tuần để hiểu radio button 47 dòng dựa trên Radix
Trong khi cách vanilla chỉ cần một ngày để làm và 20 phút để giải thích
Tất nhiên, với những sản phẩm như Figma hay Linear, nơi accessibility và keyboard navigation là rất quan trọng, thì mức độ phức tạp đó là hợp lý
Nhiều bình luận đang chỉ trích Shadcn, nhưng ngược lại tôi thấy nó khuyến khích khá tốt cấu trúc component và khả năng tái sử dụng
Cốt lõi của Shadcn là triết lý “hãy tự sở hữu và chỉnh sửa component”
Đây là một cách tiếp cận khác biệt về bản chất so với các thư viện UI truyền thống