How subito.it, Italy’s leading online classifieds platform, navigated the complexities of UI component libraries, from building everything in-house, to embracing open-source solutions.
Introduction
Building and maintaining an internal UI component library is one of those challenges that seems straightforward until you're deep into it.
At subito, we've experienced this journey firsthand, evolving from building custom components in-house, to experimenting with external Tailwindcss (often simply referred to as Tailwind) based libraries like Spark, and ultimately adopting a hybrid approach centered around Radix.
This is the story of our lessons learned and the pragmatic decisions that shaped our current approach.
Building Our Own: The Custom Component Challenge
Initially, we wanted to build our own components starting from native HTML elements as a foundation.
We had specific design requirements and wanted full control over our component behavior while building on top of standard browser elements like <select>
, <input>
, and <button>
.
However, we quickly discovered that building truly accessible and cross-browser compatible components is far more complex than it appears on the surface.
The main challenges we encountered included:
- Browser inconsistencies: What works perfectly in Chrome might behave differently in Safari or Firefox
- Accessibility requirements: Implementing proper ARIA attributes, keyboard navigation, and screen reader support requires deep expertise
- Performance optimization: Ensuring components perform well across different devices and scenarios
- Maintenance overhead: Every custom component becomes a long-term commitment that needs updates, bug fixes, and feature enhancements
We realized that maintaining these internal components was becoming expensive both in terms of development time and ongoing support.
The Tailwind-Based Library Experiment
Recognizing the need for a more sustainable approach, we began looking for external solutions.
Our first attempt involved adopting a Tailwind-based library called Spark, developed by our colleagues at leboncoin.
This seemed like a natural choice given that leboncoin faced similar business requirements and design challenges, plus, the library offered a solid technical foundation and a comprehensive component set.
Integrating this Tailwind-based library into our existing ecosystem proved more challenging than anticipated:
Application-Specific Design: It was designed primarily for complete applications rather than as a foundational library for building other UI libraries, as in our case, where we maintain a UI library used across many microsites.
Tailwind Adoption Challenge: Adopting the Tailwind-based library forced us to integrate Tailwind into our web application, even though we hadn't officially approved it as a development tool.
We were implicitly opening the doors to its usage throughout the codebase.
This generated significant confusion among developers who were left wondering: "Can I now use Tailwind utilities throughout the codebase, or should I continue writing CSS the traditional way?"
Additionally, integrating Tailwind forced us to modify our existing CSS reset and baseline styles to make them coexist with Tailwindcss's own reset styles, adding another layer of complexity to our styling architecture.
The Path to Radix: Finding the Right Foundation
After analyzing the complexities introduced by the Tailwind-based library approach, we decided to step back and reconsider our strategy. This led us to Radix as our core solution.
It proved to be the ideal foundation for several reasons:
Maturity and Reliability: is an incredibly mature library with a strong track record of stability and performance.
Accessibility First: Built with accessibility as a core principle, it handles all the complex ARIA attributes, keyboard navigation, and screen reader support that we struggled with in our custom components.
Community and Contribution: The open-source nature allows us to contribute back to the project and benefit from a large community of developers and maintainers.
AI-Friendly Development: As a widely adopted library, it has excellent AI tooling support. Its popularity means that AI agents and coding assistants have extensive knowledge of Radix patterns, making it incredibly easy to build solutions on top of it with AI assistance.
However, we noticed that Radix and our UX needs are not always perfectly aligned, so we decided to adopt a hybrid strategy.
Our Hybrid Strategy: Radix + Selective Integration
Thanks to Radix modular nature, we are not forced to adopt the entire library but can include only the components we truly need.
This gives us the flexibility to rely on Radix where it provides the most value, while integrating other specialized libraries whenever a specific component offers a better fit for our expectations.
In this way, we embrace a hybrid strategy that combines the robustness of Radix with the freedom to choose the most suitable solution for each case.
For example, since Radix did not provide a dropdown that fully met our needs, we chose to integrate React Select instead of building our own.
Conclusion
Our journey in building a UI library highlighted some key lessons:
- creating truly accessible components in-house is costly and time consuming, so leveraging open-source solutions is often the smarter move.
- If you’re not ready to fully commit to Tailwind CSS, avoid libraries tightly coupled with it, mixing approaches usually adds complexity instead of value.
- Radix proved to be a fantastic foundation: it offers mature, accessible, and behavior-driven components without dictating styling. We read about the Radix maintainers moving on, but we agree with what the author of Shadcn pointed out here.
- Mixing and matching the best tools from different open-source libraries, helped us strike a balance between control, quality, and maintainability.
Beyond technology, this journey also sparked a process shift: while our UX/design team used to propose brand-new components, the focus is now on identifying the most suitable ones already available in the open-source ecosystem, contributing when possible, and adapting them to our design system.
This change transforms the relationship between design and engineering:
- before, the model was focused on creation,
- now, it’s increasingly about curation, research, and adaptation.
At the end of the day, the goal is simple: deliver great user experiences without reinventing every wheel along the way.
Top comments (0)