React component libraries are becoming increasingly popular, and for good reason. They are great tools that guarantee consistency and reusability of UI elements both within and across digital products, all while having the potential to significantly reduce development time and effort.
In this tutorial, we will outline different approaches to building a custom component library. But first, I have a question for you: do you really need your own component library? Let’s talk about the benefits and trade-offs of customizing one.
One of the greatest benefits of using a component library is that it guarantees our components look and behave consistently throughout the app. UI inconsistencies can seriously damage user experience and become the source of frustration.
Components that live in a component library are by definition reusable. This is true both within and between web apps. If a change is needed, we change it once in the component library and it’s then reflected everywhere.
It is important to ensure our web apps are as accessible as possible. With the use of a component library, a basic level of accessibility can (and should) be built directly into our components.
A component library can become a huge time-saver. Having a repository of ready-to-use components means less time to build new features. What’s more, there is less communication overhead between designers and developers because UI behavior is pre-defined.
Component libraries are a great way to create useful and comprehensive documentation about UI components. For more details on this, check out my article on self-documenting React components.
A well-built component library might be useful to others. It can be open-sourced and shared with the web dev community.
Building a custom component library sounds great, doesn’t it? But there is always a flip side.
Building a component library requires a significant cross-functional, upfront effort involving design, product, and development teams. It is a huge undertaking that requires the appropriate level of resources and expertise to get all the benefits.
Maintaining a custom component library is a continuous effort. It requires constant attention to keep it up to date. Involving an open-source community creates even more overhead.
The use of a component library adds complexity to a project setup. Normally, in order to be reusable across projects, the component library lives separately from our apps and is installed as a dependency. That means every time there is a change, the component library needs to be re-published and all dependent apps updated.
A component library by design puts many constraints on what is possible to do with our components. This is usually a good thing. However, there are certain cases when the need for customization is inevitable. Striking the right balance between the flexibility and simplicity of component APIs can be extremely challenging.
Given the above, it seems that for most projects, building your own component library from scratch is simply not worth the time and effort. The good news is, you might not have to!
There are a multitude of ways to balance the benefits and trade-offs of creating component libraries, and the React ecosystem is here to help.
As implied by the name, with this approach, we create everything ourselves — from UI rules and patterns to the components themselves. We use React, perhaps together with our favorite CSS-in-JS library (if you are into that kind of thing 😉), but everything else is up to us to build.
Building a React component library from scratch is mainly suitable for complex and somewhat idiosyncratic digital products, spanning across multiple web apps, and with a large enough development team to be able to withstand the cost.
Building our own structure and constraints when it comes to the basic building blocks of our component library — colors, typography, spacing, shadows, etc. — is both difficult and, in most cases, unnecessary. Great solutions already exist!
What immediately comes to mind in this context is Tailwind CSS. It allows us to build complex UIs with a limited and constrained-based set of style utilities. Tailwind is easy for developers to adopt and get familiar with and is also extremely customizable and flexible.
If we opt for a CSS-in-JS solution and styled-components in particular, another approach to introducing some useful constraints is to use Styled System. With it, we can structure our theme in extremely useful ways and take advantage of a number of utilities while retaining significant control when it comes to building our components.
This approach is suitable for projects with UI components that require a lot of flexibility and freedom to customize. Before settling on a solution, ensure the constraints that are introduced are suitable for the project’s particular use cases and are in line with design and development requirements.
There are certain React components that are notoriously difficult to create from scratch. Think of a select component that needs to support features like search, multi-select, creating your own option is non-exists, keyboard control, and responsiveness.
What about a date picker with a calendar, keyboard controls, masked input, and validations that are also accessible? And how about a tooltip that is animated, customizable, and always positions itself correctly? Even widely used components like modal and tabs can be challenging to create properly from scratch.
A great way to save time and effort while creating a component library is to leverage React’s ecosystem for these more complex components. Going back to the list from above, here are some examples.
When it comes to a select component, react-select comes with all of the already mentioned features and more out of the box. For date pickers, there are also some great options, depending on the use case, including react-date-picker, react-datepicker, and AirBnB’s react-dates.
When it comes to tooltips, react-tippy has us covered. Finally, Reach UI is an example of a library that provides truly accessible and customizable components like modals, tabs, menus and so much more. They can all be installed separately, so choose all or only some of them to add to your project.
On the flip side, adding third-party components to your library comes with some trade-offs.
Using this approach takes a huge load off the component library development team. Still, when deciding to integrate third-party components, you must consider the long-term impact and make sure, to the extent that it is possible, that these components correspond to the project needs.
It is often the case that the time and resources we are willing to invest in a component library for our project simply does not equate to what is needed to implement the approaches above. This could be the case if the scope of our project is somewhat limited or, even with bigger projects, if the UI components we need are relatively standard.
We still want to be able to style and somewhat customize our components, but we are happy to rely on the sensible component APIs determined by an external base component library. Luckily, there are several options to choose from in the React ecosystem. While comprehensive lists of React component libraries can be found elsewhere, I consider Chakra UI to be a suitable choice for this approach in the vast majority of cases.
Using an existing component library for our project is the way to go if we value development speed while being confident that future requirements will not clash with our choice.
Finally, we can build our project with a component library that is ready to use. These solutions, while being somewhat customizable, provide us with everything we need straight away, from color schemas and a theme to a comprehensive collection of components.
A prime example of this is Google’s Material UI. This library is truly extensive, which makes it perfect if you want to start building your React app with minimal setup. Because of this, Material UI is, without a doubt, my tool of choice when building prototypes and proofs of concept. It is also handy during interview tasks when time is of the essence.
There are, however, significant downsides to tightly coupling a real-life project to a strongly opinionated component library. Let’s break it down.
The approach we choose when building (or not building) our custom component library is a strategic decision that will impact our React project from start to finish. Understanding the implications of this choice and being able to adequately assess benefits and trade-offs is of paramount importance. I hope the above overview helps us be better equipped for the challenge.
If you found this article useful, follow me on Twitter for more tech content!
Install LogRocket via npm or script tag.
LogRocket.init() must be called client-side, not
ElectricSQL is a cool piece of software with immense potential. It gives developers the ability to build a true local-first application.
Leptos is an amazing Rust web frontend framework that makes it easier to build scalable, performant apps with beautiful, declarative UIs.
We spoke with Dom about his approach to balancing innovation with handling tech debt and to learn how he stays current with technology.