Recipe kits, a great alternative to installable libraries
A few years ago, my team was starting a new project which needed a fairly comprehensive set of components from the outset, and we didn’t have the resources to build them from scratch. Our existing components were geared towards a few fairly specific use cases, so we lacked comprehensive coverage of common UI patterns, especially those for information-dense UIs like dashboards and power user tools.
It fell upon me to make a quick decision about which component library to use. We were using React, and there was a fairly large selection of libraries available. Some felt too heavily branded, in the sense that using the components as intended would give results that looked very much like the products of well-known companies; others didn’t have a broad enough set of components, or bundled too many transitive dependencies.
I made my choice with a fair amount of haste, and we were reasonable happy with things for a time. It was only a bit later than I realised that the accessibility aspect of the library wasn’t great, and I came to regret my decision.
Since then, I’ve worked with other popular component libraries within the React ecosystem. These did a better job with a respect to accessibility, but typically came with their own warts. The most common issues were related to customisation, I very often found myself trying to customise the components in some way to revealed bugs in the libraries themselves, or that the component APIs simply didn’t allow. This isn’t an indictment of the libraries, but rather reveals the challenges of deferring such a key part of your front-end stack to an external team.
My constraints when it comes to component libraries end up looking a little like this:
I need to start building user interfaces today. I’m perfectly capable of building all the necessary components from scratch, but that’ll slow me down from building what I’m actually here to build
Comprehensive. Having to regularly stop product work because of missing components isn’t ideal.
Accessible. Getting accessibility right is hard, so if someone’s already done this, it means I’m less likely to overlook it or give in to the pressure to skip it.
Dependencies that align with what I’m already using. This is a tricky one, but I likely already have a favoured set of dependencies for things like animation, headless UI, styling and so on. So if my choice of component library brings in a heap of different (but equivalent) dependencies, that’s a waste.
Customisable, without bloat. Quite often, 3rd party components suffer from either being too customisable or not customisable enough. Too customisable means we’re paying a file size cost for functionality we’ll likely never use. Too little power to customise and we find ourself working around the components rather than with them.
Grows with you. A component library is a set of heavily-used abstractions that you'll take with you as your systems evolve. It's worth trying to make early architectural decisions that don't rapidly become anchors to progress.
When I consider these constraints, I find myself concluding that perhaps the problem is that an installable package is the the wrong format for distributing components. Maybe there’s another way? Open source doesn’t necessitate that you be able to install the code as a packaged dependency, just that you have access to the source. I’ve often scoured the internet for examples of solutions to my problems; finding the ones that fit best, copying them and adapting them as needed. What if component libraries worked like this?
Happily, in a way, some actually do. I’m aware of two so far:
Tailwind UI - A premium (paid) suite of recipes containing sample implementations of a wide range of UI components in different styles — all built using Tailwind CSS, as well as examples where they’ve been combined into more complete designs or websites. Each recipe is available as HTML (with instructions for how to add interactivity), React, or Vue. The React and Vue examples use Headless UI for interactivity.
shadcn/ui - A relatively new (and free) collection of component recipes, not too dissimilar to Tailwind UI, but with a more pared back default appearance, and specifically targeting React. The interactive examples make use of Radix UI, and in many ways serve as excellent demos for that library too. The author has gone much further than this, exploring novel ways to make the components installable without turning them into dependencies, and using AI to generate variations of the components that better integrate with your existing stack.
One consequence of this paradigm is that once you've added a recipe to your own codebase, it becomes your code. This could be seen as a disadvantage, as bug fixes and other changes are no longer just a dependency upgrade away. But the flip side of this is that you're able rapidly incorporate your own fixes and changes. Your component library will end up being a key part of your design system, I'd contend that's something you really want to have ownership of.
I suspect the key decision point as to which distribution method is best will boil down to the nature of what problem the code is solving and how it relates to the competencies of your team. If it's something you likely could make yourself, and you're looking for existing solutions primarily as a time-saving measure, this paradigm becomes quite compelling. Component libraries fit this description well for me. If it's something a bit more involved, perhaps a state management library and beyond, I'd likely suggest that sticking with the tried-and-true installation method is the right approach.
Overall, I’m excited about this approach, it seems to address the problems I’ve raised. I’d encourage more authors of component libraries to consider this “not actually a library” recipe kit paradigm as an alternative to installation.