Working With React Component Libraries for Interactive Controls
Lorem ipsum dolor sit amet, consectetur adipiscing elit lobortis arcu enim urna adipiscing praesent velit viverra sit semper lorem eu cursus vel hendrerit elementum morbi curabitur etiam nibh justo, lorem aliquet donec sed sit mi at ante massa mattis.
Lorem ipsum dolor sit amet, consectetur adipiscing elit ut aliquam, purus sit amet luctus venenatis, lectus magna fringilla urna, porttitor rhoncus dolor purus non enim praesent elementum facilisis leo, vel fringilla est ullamcorper eget nulla facilisi etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus in ornare quam viverra orci sagittis eu volutpat odio facilisis mauris sit amet massa vitae tortor condimentum lacinia quis vel eros donec ac odio tempor orci dapibus ultrices in iaculis nunc sed augue lacus
At risus viverra adipiscing at in tellus integer feugiat nisl pretium fusce id velit ut tortor sagittis orci a scelerisque purus semper eget at lectus urna duis convallis. porta nibh venenatis cras sed felis eget neque laoreet libero id faucibus nisl donec pretium vulputate sapien nec sagittis aliquam nunc lobortis mattis aliquam faucibus purus in.
Nisi quis eleifend quam adipiscing vitae aliquet bibendum enim facilisis gravida neque. Velit euismod in pellentesque massa placerat volutpat lacus laoreet non curabitur gravida odio aenean sed adipiscing diam donec adipiscing tristique risus. amet est placerat in egestas erat imperdiet sed euismod nisi.
“Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum”
Eget lorem dolor sed viverra ipsum nunc aliquet bibendum felis donec et odio pellentesque diam volutpat commodo sed egestas aliquam sem fringilla ut morbi tincidunt augue interdum velit euismod eu tincidunt tortor aliquam nulla facilisi aenean sed adipiscing diam donec adipiscing ut lectus arcu bibendum at varius vel pharetra nibh venenatis cras sed felis eget dolor cosnectur drolo.
Why do we need a component library in the first place?
Developers who have strong proficiency in the fundamentals of HTML and CSS might feel that importing a component library is redundant, or might think it's only needed for achieving a decent appearance when you can't afford a UI designer and a frontend dev to implement the design. After all, it's not too hard to put together elements in a flexbox or to apply colors, padding and margin consistently.
However, when you actually try to craft your components from the native building blocks of the web, you will probably run into various issues. You might be fine as long as you only deal with simple layout and typography, but once you get into more complex elements, especially elements that have some behavior – like form controls or toast notifications, you'll easily run into issues of broken functionality, accessibility concerns and unhandled edge cases.
For example, a toast notification sounds like a simple case – it appears in response to some event and goes away after a predefined timeout period. But there's actually a lot of logic there:
Handling these features is definitely doable, but you don't want to spend a lot of time on that. You want to focus on your project, on your product, rather than hunting edge cases of common UI elements. That's where component libraries come to our aid. They provide us these features "out of the box" or offer the means to easily implement them.
But that's just one aspect of the equation. Once you decide to use a component library, you still need to decide which one, and the choice doesn’t come without cost. The more opinionated the library is, the more it provides you production-ready stuff – the harder it is to customize it – to override specific styles or specific behavior. You want your component library to assist you, not to be an enemy you're struggling to fight against.
It can be said that component libraries provide up to three layers of predefined building blocks:
In this series of articles we'll first see an example of building something with no component library. Then we’ll check the Radix-UI library, which provides the first layer of the three – the functionality layer – while leaving the whole job of styling to the developer. Finally, we'll see two different higher-level libraries with different approaches, both built on top of Radix’s primitives: The first is Radix Themes – coming from the same creators, and the second is shadcn – a no-installation component repository.
To understand the place of component libraries better, let’s craft a tiny React app that shows the capital cities of different countries. It’s a very simple one – basically just a dropdown list of countries to choose from and a single sentence informing what the capital city of that country is. Here’s the full code of the App component:
1import { useState } from "react"
2import SelectCountry from "./components/SelectCountry";
3import CountryCapital from "./components/CountryCapital";
4import "./App.css";
5
6const capitals = {
7 Britain: "London",
8 France: "Paris",
9 Israel: "Jerusalem",
10}
11
12function App() {
13 const [country, setCountry] = useState("Britain");
14 const capital = capitals[country];
15
16 return (
17 <div className="app-container">
18 <SelectCountry
19 countries={Object.keys(capitals)}
20 onCountrySelected={setCountry}
21 />
22 <CountryCapital
23 country={country}
24 capital={capital}
25 />
26 </div>
27 )
28}
29
30export default App
Our app has a state (the chosen country), a data object mapping between each country and its capital, and two components: <SelectCountry> for the user to choose a country out of the list, and <CountryCapital> to display the capital city of the chosen country.
Of course, for our app to work, we need to actually implement these two components. There is already a <select> control as a native HTML element, so let’s just use that, without any component library:
1export default function SelectCountry({ countries, onCountrySelected }) {
2 return (
3 <label>
4 Choose country:
5 <select
6 onChange={(event) => {
7 onCountrySelected(event.target.value);
8 }}
9 >
10 {countries.map((country) => (
11 <option key={country} value={country}>
12 {country}
13 </option>
14 ))}
15 </select>
16 </label>
17 );
18}
1export default function CountryCapital({ country, capital }) {
2 return (
3 <p>
4 The capital city of <strong>{country}</strong> is <strong>{capital}</strong>
5 </p>
6 )
7}
One of the nice things about using native HTML controls is that even without adding any CSS at all, it “just works”. Sure, it doesn’t look very aesthetic, but nothing is broken.
Splashing some CSS in, we can turn that into a nice-looking widget:
That seems good! We might still want to adjust the colors and the fonts, perhaps add some decoration, display the country flag, etc - none of this is complicated to customize.
Well… none except that dropdown box itself..
The native <select> element rendered by the browser offers few styling options, and we can apply almost no styling to the options in the list. Doing something “advanced” like adding a flag icon next to each country name or marking the currently selected item is probably entirely out of the question.
If we do want something that looks better, we might be tempted to hide the native control entirely and display something of our own. But then we’ll lose the built-in semantics and easily run into issues of accessibility and functionality. We need some solid ground for it, and that’s what we’ll look into in the next article of this series.