/

Icon Libraries for React: Consistency, Weight and Licence

An icon set is a design system, not a lookup. Pick one family, and know the three things that break tree shaking.

Serafim Korablev
Serafim Korablev
@korablev

Icons are the component category where the wrong decision is invisible for months and then shows up as three hundred kilobytes in a bundle and four visual styles on one screen. Both problems have the same root: an icon set is a design system, and most projects treat it as a lookup.

The icon catalogue indexes 29,107 icons across the common families, which is a useful way to see how differently the families are drawn before committing to one.

Pick one family and stay in it

The rule that matters more than which family you pick. Icon sets differ in stroke width, corner radius, optical size and how much detail they carry, and mixing two is visible immediately even to people who cannot say why.

If you need an icon the family does not have, draw it in the family's style rather than importing a second set for one glyph. That is a twenty-minute job and it is cheaper than the alternative forever after.

Choosing by what you need

A general outline set covers the vast majority of product interfaces: navigation, actions, states. This is what most projects want and where the popular families sit.

A filled companion matters if your design uses fill to signal an active state. Check the family has both weights before committing, because inventing filled versions later is unpleasant.

Brand and logo marks are a separate problem: they are not in general icon sets, they have licence terms of their own, and they change. Keep them apart from your interface icons.

Illustration is not iconography. A spot illustration in an empty state is a different asset with different rules, and pulling it from an icon set produces a picture at the wrong scale.

The weight question

The part people get wrong. An icon library that exports one component per icon is only as heavy as what you import, provided the build can tree-shake it: import the three icons a file uses, not the whole set.

Three things break that. A barrel import that pulls the index. A dynamic icon lookup by name, which forces the bundler to keep every icon because it cannot know which one runs. And a set distributed as a single font or sprite, which is all-or-nothing by construction.

The dynamic-name case is the common one in a design system, and the honest fix is an explicit map of the icons you actually allow, which doubles as the list of what is in the system.

Rendering them properly

SVG, inline, sized in em. An icon that scales with its text stays aligned when the type scale changes.

currentColor for the fill or stroke. That is what makes an icon inherit the button's colour in every state without a second class.

Decorative icons are hidden. An icon beside a text label is decoration: aria-hidden="true", so a screen reader does not announce it twice.

Icon-only buttons need a name. aria-label on the button plus a tooltip for pointer users, as the button guide sets out. A row of unlabelled icons is the most common unreadable toolbar on the web.

Optical size, not just size. A 16px icon drawn at 24px scale looks thin and misaligned. Families that ship multiple optical sizes are worth using at the size they were drawn for.

Licences

Worth thirty seconds before the landing page ships. Most popular sets are permissive, some require attribution, and brand marks are almost always restricted regardless of the set that includes them. The icon catalogue shows the family and its terms alongside each icon, which is faster than reading four repositories.

Where else to look

The honest list, because the answer is not always us:

SourceBest forTrade-off
A single general familyConsistency, which matters more than coverageOccasional gaps you draw yourself
The icon catalogueComparing families side by side before committingA browser, not a package
A custom setIcons that are unmistakably yoursSomebody has to draw and maintain them
Icon fontsOne request, universal supportAll-or-nothing weight and worse rendering

Taking one

Icons copy straight out of the catalogue as SVG or as a component. For everything else, installing goes through the shadcn CLI against our registry:

bash

That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue.

Browse icons →

Frequently asked

Can you mix icon sets?
Not without it showing. Families differ in stroke width, corner radius, optical size and detail, and two on one screen read as a mistake even to people who cannot name the cause. If a family lacks an icon, draw it in that family's style rather than importing a second set for one glyph.
Do icon libraries make bundles heavy?
Only when tree shaking is defeated. Importing named icons from a set that exports one component each is as heavy as what you use. Three things break it: a barrel import, a dynamic lookup by name, which forces the bundler to keep everything, and a set shipped as a single font or sprite.
How should icons be rendered?
Inline SVG sized in em so it scales with its text, coloured with currentColor so it inherits state without a second class, marked aria-hidden when it sits beside a text label, and used at the optical size it was drawn for. Icon-only buttons need an aria-label and a tooltip.
Do icon licences matter?
Enough for thirty seconds before shipping. Most popular sets are permissive, some require attribution, and brand marks are almost always restricted regardless of which set includes them. Keep brand logos separate from interface icons for that reason as well as visual ones.

Published

Aug 21, 2026

Read time

4 min

Tags

GuideIconsReactDesign systems

Share