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:
| Source | Best for | Trade-off |
|---|---|---|
| A single general family | Consistency, which matters more than coverage | Occasional gaps you draw yourself |
| The icon catalogue | Comparing families side by side before committing | A browser, not a package |
| A custom set | Icons that are unmistakably yours | Somebody has to draw and maintain them |
| Icon fonts | One request, universal support | All-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:
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.
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.