2 điểm bởi GN⁺ 2026-01-21 | 4 bình luận | Chia sẻ qua WhatsApp
  • 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><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-reacthà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><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ỗilà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

 
crawler 2026-01-21

Nhàm chán và hàn lâm quá mức:

function RadioGroup({  
  className,  
  ...props  
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {  
  return (  
    <RadioGroupPrimitive.Root  
      data-slot="radio-group"  
      className={cn("grid gap-3", className)}  
      {...props}  
    />  
  );  
}  
...  

Xong rất nhanh, nhớ rất lâu:

<input type="radio" name="beverage" value="coffee" />  
 
slowandsnow 2026-01-22

Nhìn component button của react aria chắc xỉu luôn ấy, haha

 
preserde 2026-01-21

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 input type 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.

 
GN⁺ 2026-01-21
Ý 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

    • Giờ đây, vấn đề lớn hơn là bầu không khí kiểu mọi người đương nhiên xem React là mặc định
      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
    • Tôi đã làm UI hơn 15 năm bằng JavaScript thuần và DOM API
      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
    • Đây không chỉ là vấn đề của React; tôi nghĩ vấn đề lớn hơn là mọi người không muốn học CSS hiện đạ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
    • Trừu tượng hóa vốn dĩ phải là một công cụ triết học để che giấu độ phức tạp, nhưng dạo này nhiều khi nó lại làm mọi thứ phức tạp hơn
      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ể
    • Cốt lõi của React là trừu tượng hóa bằng component
      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

    • Site McMaster-Carr thường được nhắc đến như một ví dụ tốt. Cũng có thread Hacker News liên quan
    • Tôi đã làm một web app cộng tác video cách đây 15 năm, frontend gần như là cấu trúc vanilla dựa trên jQuery
      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
    • “Thế site này thì sao?” — có khi chính Hacker News là một ví dụ như vậy
    • Công ty càng lớn thì quản lý càng muốn một stack được chuẩn hóa
      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
    • UI là thứ ai cũng nhìn thấy và ai cũng có ý kiến, nên độ phức tạp có xu hướng phình ra như bi kịch của tài nguyên chung
  • 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

    • Dạo này tôi thích các UI framework không có JS (Pico.CSS, Skeleton, Bulma, Tailwind/daisyUI)
      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

    • Tổ hợp React+NextJS+Tailwind+Shadcn đúng là đỉnh cao của sự phức tạp
      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
    • Thực ra Tailwind và React không hợp nhau lắm
      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ến
    Radio button mặc định có trải nghiệm sử dụng kém trên di động

    • Thực ra native control cũng có thể style khá đầy đủ bằng CSS
      Tôi muốn biết cụ thể hơn là trên di động đã gặp vấn đề gì
    • Bài viết cũng giải thích cách style radio button bằng CSS. Đó chính là vấn đề sao?
    • Chỉ cần bọc bằng <label> và thêm padding là trên di động cũng dùng ổn
    • Riêng “select” thì vẫn khó style, nhưng còn lại thì tùy biến khá linh hoạt
  • Hầ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)

    • Nhưng về accessibility thì vẫn còn nhiều thiếu sót
      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?

    • Chỉ cần chỉnh nhẹ là có thể làm khá giống
      Có thể xem ví dụ CodePen
    • Rốt cuộc, ngay cả dưới những framework phức tạp đó thì CSS vẫn là cốt lõi
      Chỉ cần tách CSS ra rồi gắn vào một component React đơn giản là đủ
    • Như ví dụ này, nếu áp cùng CSS cho input vanilla thì độ tương thích trình duyệt cũng tốt
    • Chính tác giả bài viết đã vào nói rằng: “Ví dụ cơ bản này chỉ đơn giản được chỉnh cho hợp với style của Shadcn, còn nếu muốn thì hoàn toàn có thể tùy biến thoải mái
    • Nhưng rồi lại phải cân nhắc nên theo đuổi sự hoàn hảo đến mức nào
      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ý

    • Nhưng cũng có thắc mắc rằng một thư viện tốt chẳng phải nên cho phép dùng được mà không cần hiểu cấu trúc bên trong hay sao?
  • 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