8 things we learned building a React UI library for ~3 years
Been working on Rad UI quietly. Here's what actually matters according to us.
We’ve been silently building a React UI library called Rad UI for about three years now. Not shipping blog posts about it, not tweeting hot takes, just building, breaking things, and rebuilding them. We finally hit a point where we are confident and proud of what we've built and would love to share our experience with the community. Here's what we've actually learned.
Rad UI lives on GitHub at https://github.com/rad-ui/ui
Components are easy. APIs are hard.
An Accordion is not hard to build. An accordion API that still feels good after 200 teams use it in weird product surfaces? That’s where the pain lives. When you have a couple of low-level primitives driving the functionality, and the library is growing, and so are the low-level primitives that are going to be reused and refactored to be used with 10 other components - good luck managing growing complexities. This is where CI pipelines that ran tests for every core library change saved us from shipping breaking changes
Defaults matter more than options.
Most users don’t want infinite flexibility. They want the thing to look good, behave correctly, and not explode in production. Especially with a headless UI library, when the library is meant to be used with sub-components to be built into a full component, you’ll need a minimum number of props with sensible defaults for it to start working from day one, not one day.
Styling escape hatches are not optional.
Every app has legacy CSS, brand constraints, product quirks, and one cursed page from 2019. Your library has to survive that mess. This is the exact reason why we built Rad UI to survive. We ship a default design system along with the library for people who don’t want to design their own and just want to build something, but at its core - it is unstyled and headless.
Accessibility cannot be sprinkled later.
Keyboard nav, focus management, ARIA, screen reader behavior, disabled states, portals, modals. Bake it in early or suffer later. Writing accessibility tests is a completely different beast; breaking this means breaking the library core itself. Also, keeping up with WCAG guidelines and updates is another task by itself. Accessibility is a serious feature, and we take it very seriously, sometimes annoying, but important.
Design systems fail more from adoption than architecture.
Even the best component is worthless if teams don’t trust it, the documentation is thin, or migration feels like dental surgery. Anyone who has built frontend products in a startup or high-velocity environment knows this firsthand.
Design systems are living systems. They evolve continuously, and you either evolve with them or work around them to ship.
Our default design system isn’t perfect, and it never will be. What made it durable was committing early to a strong API design and staying disciplined about it over the years.
AI changes the bar.
Documentation isn’t just for humans anymore. If agents can’t reliably understand your components, your developer experience is already behind.
We’re continuously improving our docs for both people and machines, while also strengthening discoverability through SEO.
Stability is a feature.
Boring APIs, predictable releases, clear migration paths.
Sexy? No.
Necessary? Absolutely.
A good UI library is not a folder of components.
A great design system is never just components. It’s taste, constraints, accessibility, documentation, escape hatches, visual consistency, and hundreds of small decisions users rarely notice.
That’s why shadcn/ui took off and became a default choice for many teams. We learned a lot from its philosophy, but our direction is different, and our constraints are different.
Unlike shadcn/ui, which typically brings in a wider set of package dependencies, Rad UI is built as a more unified system around a single library without the dependency soup. Our devs don’t need to worry about managing a package on their own if any of the underlying packages are abandoned or become inactive.
Still learning. Still rebuilding. Still finding cursed edge cases in dropdowns.
Feel free to try out Rad UI and critique or commend what we’ve built.

