The Overcomplexity of the Shadcn Radio Button
Recorded: Jan. 20, 2026, 10:03 a.m.
| Original | Summarized |
The Incredible Overcomplexity of the Shadcn Radio Button Paul Hebert GitHub The Incredible Overcomplexity of the Shadcn Radio Button import * as React from "react"; import { cn } from "@/lib/utils"; function RadioGroup({ function RadioGroupItem({ export { RadioGroup, RadioGroupItem }; Radix Primitives is a low-level UI component library with a focus on So Radix provides unstyled components, and then Shadcn adds styles on top of Okay, instead of a radio input it's rendering a button with an SVG circle inside If you can use a native HTML element or attribute with the semantics and Despite that, Radix is repurposing an element and adding an ARIA role instead of appearance: none removes the radio button's default styling allowing us to Here's an example implementation: /* Recreate the circle container */ /* Center our dot in the container */ /* Use a pseudo-element to display our "dot" */ /* And display it when the radio button is checked */ Fancy a game? |
Paul Hebert’s article critiques the excessive complexity of implementing radio buttons using the Shadcn UI framework, contrasting it with the simplicity of native HTML elements. He begins by recounting his initial assumption that updating radio buttons would be straightforward, relying on the basic `<input type="radio">` element. However, upon examining his workplace’s codebase, he discovered that the team had adopted Shadcn components—specifically `<RadioGroup>` and `<RadioGroupItem>`—which are built on top of Radix UI, another library. This layered approach led to a convoluted implementation involving multiple dependencies, custom styling, and accessibility considerations that Hebert argues unnecessarily complicate a seemingly simple task. The Shadcn components, as presented in the article, require importing three external libraries: `@radix-ui/react-radio-group`, `lucide-react` for icons, and a custom utility file (`cn`) for styling. The code itself spans 45 lines, with extensive Tailwind CSS classes managing visual properties such as borders, shadows, and transitions. Hebert highlights the inclusion of a third-party icon library to render a circle, which he finds perplexing given that native CSS solutions like `border-radius` or SVG elements could achieve the same result without external dependencies. He questions the rationale for such a design decision, suggesting that it introduces unnecessary overhead and obscures the underlying functionality. A deeper exploration reveals that Shadcn’s radio button components are built using Radix UI, a framework that provides low-level, unstyled components. Radix itself is described as a library that prioritizes accessibility and customization but requires significant boilerplate code. Hebert notes that Radix’s `RadioGroupPrimitive` component, which underpins Shadcn’s implementation, involves 215 lines of React code and imports seven additional files. This complexity is further compounded by the use of ARIA (Accessible Rich Internet Applications) attributes to simulate the behavior of a native radio button. For instance, Radix repurposes a `<button>` element and assigns it an `aria-role` of "radio," effectively tricking assistive technologies into interpreting it as a radio button. While ARIA is designed to enhance accessibility, Hebert warns against its misuse, citing the first rule of ARIA: "If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so." By circumventing native elements, Radix and Shadcn introduce a layer of complexity that undermines the simplicity of HTML’s built-in capabilities. Hebert also points out a peculiar quirk in Radix’s implementation: the inclusion of a hidden `<input type="radio">` element, which is only rendered if the component is used within a `<form>`. This behavior, while technically functional, further muddies the waters by conflating native and custom implementations. The article argues that this approach forces developers to navigate multiple layers of abstraction—Shadcn, Radix, and the browser’s native HTML—when a straightforward solution would suffice. The author then shifts focus to the broader debate over styling radio buttons. He challenges the notion that native radio buttons are inherently difficult to style, citing modern CSS techniques such as `appearance: none`, `::before` pseudo-elements, and `:checked` selectors to create custom visual designs. A sample CSS snippet demonstrates how a radio button can be restyled with minimal code, eliminating the need for external libraries or JavaScript. Hebert emphasizes that this approach is not only simpler but also more efficient, as it avoids the performance penalties associated with loading and executing additional JavaScript. He contrasts this with Shadcn’s implementation, which relies on 30 Tailwind classes to manage styling—a stark contrast to the conciseness of native CSS. Despite acknowledging the appeal of component libraries like Shadcn, Hebert expresses frustration with their tendency to prioritize abstraction over simplicity. He concedes that developers often turn to such tools to streamline workflows and reduce the cognitive load of managing intricate CSS or accessibility considerations. However, he argues that this convenience comes at a cost: increased complexity, higher maintenance overhead, and potential performance issues. The article highlights how the reliance on layered libraries forces developers to grapple with hundreds of lines of code and multiple dependencies, all to replicate functionality that could be achieved with native elements. Hebert concludes by advocating for a return to simplicity, urging developers to leverage the browser’s built-in capabilities whenever possible. He acknowledges that web development is inherently complex, but contends that certain tasks—like implementing radio buttons—should not require convoluted workarounds. His critique extends beyond Shadcn and Radix, touching on a broader trend in modern web development where abstraction often overshadows practicality. By emphasizing the elegance of native HTML and CSS, he calls for a more mindful approach to tooling that prioritizes clarity, efficiency, and maintainability over unnecessary complexity. The article serves as a cautionary tale about the trade-offs involved in adopting component libraries, particularly when they obscure the fundamental principles of web development. While tools like Shadcn and Radix offer valuable abstractions, Hebert’s analysis underscores the importance of understanding their underlying mechanics and questioning whether they truly address real problems or simply complicate existing solutions. In an era where performance and accessibility are paramount, his argument for simplicity resonates as a timely reminder that sometimes the most effective solutions are the ones closest to the metal. |