NOTE: This is only React stuff. Vuetify and Vant libraries make me want to switch to Vue! Angular’s implementation of Material design is arguably better than React’s.

What am I looking for?

  • Don’t pollute the global css namespace with classNames like “grid” or “padded”
  • Don’t pollute the props namespace
  • Minimal bundle size (including any dependencies that I’d need in order to use it)
  • Some components or utilities should work on server-side. Color scheme at least.
  • Preferably use CSS variables to style CSS. They work on initial page load - no need to wait for JavaScript to be ready - so no flicker on page load. Also, they can be changed for just a part of a page - so a header or hero can be dark mode, but a popup or form content can be light.
  • Preferably makes use of dataset and aria attributes instead of complicated JS workarounds.

Leaders:

A headless UI + your choice of styling system

Ariakit is the best headless UI! Radix is also great. HeadlessUI gets an honorable mention. Or use a combination. As long as it’s tree-shakeable, anything goes! Downshift and react-select are great choices for making a custom-branded select/dropdown.
Then choose styling library. Many people prefer Tailwind. People even unknowingly call it a React UI library. But it’s just a CSS stylesheet, a lot of tiny CSS classes.
SCSS Modules are functional, efficient, light, and keep your code maintainable and readable. That’s the best choice, and is finally getting some recognition by experienced developers who are tired of constantly being tired and frustrated struggling with CSS-in-JS limitations.
If you’re going to add custom styling and branding to your components anyway, then it’s actually much easier to start from scratch than to struggle to override the 3rd party styles your library came with (Material UI, Ant Design, Chakra, Bootstrap, Atlassian, Zendesk, etc).
Check out Ariakit. It’s got great components that are easily customizable!
Image without caption

MUI (Material UI)

Most popular React library, because it’s great. CSS haters can use the sx prop.
The components look good, but you must be OK with the Material design style. Sure, you can customize it, but there’s still that fundamental Material look.
Tip for CSS experts — no more struggling with specificity! MUI have figured it out.
  1. Wrap your app in <StyledEngineProvider injectFirst>.
  1. They provide a :global selector to increase specificity of your custom styles.
  1. Style the inner elements directly, without fancy selectors. Add prop to the component: slotProps={{ thumb: { className: styles.thumb } }}
Also, you like CSS variables? It’s now supported! They prevent flickering of the screen from white to black when using dark mode (because now your visitor does not need to wait for JS to load)

Chakra

Their Box and styled-system makes it easier for Typescript developer to get started, compose components, and keep track of props and styles. It’s great for both beginners and for expert programmers who hate CSS, and would prefer to do everything in Typescript.
Unfortunately, it encourages you to pass each individual CSS property as a React prop, to each component. This does have some benefits, but makes a complete mess of the namespace and already difficult to read JSX. If you use this, you better be splitting up your components into tiny bite-size pieces (only a couple HTML elements in each). No longer an option to write long React component files with multiple HTML elements. Overall, it encourages good practices, but implementation feels too rigid and chaotic at the same time.
There is method to this madness. Passing each CSS prop as a JSX prop makes it easier to override individual styles. I guess when they were making the UI library, this was a main concern - that the users of it would be able to easily override the default styles. But CSS classes do this just as well, don’t have the drawbacks (don’t pollute the namespace), and work on server-side too!
Fortunately, it supports the sx prop just like MUI (both use Emotion under the hood). So if you’re also troubled by dumping every CSS prop next to your data and logic props, use sx.

Ant Design

Version 5 uses CSS-in-JS, so no more global stylesheet! Unfortunately, they did not optimize it. Idk if they’re still working on it. But the size of each import is huge, even if it a simple component.
This is frustrating, because otherwise Ant design for React is amazing! So much functionality!
⚠️ Only light-mode. No dark-mode option. And, it’s difficult to customize the Ant styles. You’ll have to use a lot of !important!

Looks promising, but I didn’t have time to try it:

Dishonorable mentions:

Looks very nice! Very minimal, yet powerful. Easy to customize the style.
Uses a CSS stylesheet with very “semantic” names that make a lot of sense. Unfortunately, it does pollute the global namespace even worse than Bootstrap. No fancy CSS-in-JS making gibberish names. Instead, you’ll have to be careful that your app/site does not use classNames like “ui”, “vertically”, “padded”, “grid”, “row”. Its no good!
⚠️ Only light-mode. No dark-mode option. You’ll have to add dark styling yourself for every element. Although it’s easy - just open their CSS file, and make a dark version of it. Maybe I’ll do that and contribute to the open-source project!
You can use Semantic UI as a Headless UI. Just don’t include their CSS file. Make your own.

Bootstrap

Pollutes the global name space like Semantic UI. But doesn’t look as good. So, no.
Incomplete for most apps. Could be useful if you need only those available components.