Skip to content
Icons

Icons — Component Library Guide

One import surface for thirty icon sets, from Font Awesome to Lucide, shipped as tree-shakeable React components with no font files and no sprite sheets.

TypeScript MIT
8.6M weekly downloads
12.6k GitHub stars
2.1 kB min+gzip bundle size
v5.7.0 latest version

Figures above measured on from the npm registry (latest version, last-week downloads) and the GitHub API (stars). Not copied from the project's own README, and re-measured rather than edited by hand, so the date tells you exactly how old these numbers are.

How the bundle size was measured. We installed react-icons@5.7.0 on its own, bundled export * from 'react-icons' with esbuild (esm, browser, minified, production), marked React external because your app already has it, and gzipped the result. Measured . This is the ceiling, not the typical cost: it imports the entire public surface with no tree-shaking credit. Pull one component from a well-shaken library and you'll pay a fraction of it. We publish the ceiling because it's the number we can reproduce, and reproducing it is the point.

iconssvgtree-shakingfont-awesomeheroicons

TL;DR: React Icons repackages roughly thirty icon families as React components behind one dependency. Import from the family's subpath so tree-shaking works, size and color follow CSS by default, and nothing loads a font file. Browser: react-icons.github.io/react-icons.

What is React Icons?

React Icons is a build of many popular icon sets into a single npm package, with each set behind its own import path. Font Awesome, Material Design, Bootstrap, Feather, Heroicons, Lucide, Simple Icons, Phosphor, Remix and others are all present, and each icon arrives as an ordinary React component rendering inline SVG.

The design goal is that adding an icon costs one import and roughly a kilobyte, rather than a font file, a sprite sheet, or a build step.

When to use it

Use it when a product needs icons from more than one family. That happens more often than it sounds: a general UI set for the interface, plus brand marks for third-party logins, plus a technical set for file types. Installing three packages and reconciling three prop APIs is worse than one.

Use something else when you know your family. If the design system says Lucide, lucide-react gives you the same icons with a smaller dependency graph and faster type checking, because your editor isn't indexing thirty sets.

Key Features

  • Around thirty icon families behind one dependency
  • Inline SVG components, so no font loading and no FOUT
  • 1em sizing and currentColor fill, so icons follow surrounding text
  • IconContext.Provider for setting size, color and className across a subtree
  • Per-set import paths that let bundlers drop everything you don't use
  • Types included, so every icon is a typed component

Installation

npm install react-icons

Import from the two-letter subpath of the set, never from the package root:

import { FaGithub } from 'react-icons/fa';
import { LuSettings } from 'react-icons/lu';

Using Icons

import { FiTrash2, FiEdit2 } from 'react-icons/fi';

export function RowActions({ onEdit, onDelete }: {
  onEdit: () => void;
  onDelete: () => void;
}) {
  return (
    <div className="row-actions">
      <button onClick={onEdit} aria-label="Edit row">
        <FiEdit2 />
      </button>
      <button onClick={onDelete} aria-label="Delete row">
        <FiTrash2 />
      </button>
    </div>
  );
}

Both buttons carry the accessible name. The icons stay silent, which is what you want when the control already says what it does.

Setting Defaults For a Subtree

import { IconContext } from 'react-icons';

export function Toolbar({ children }: { children: React.ReactNode }) {
  return (
    <IconContext.Provider value={{ size: '1.25rem', className: 'toolbar-icon' }}>
      {children}
    </IconContext.Provider>
  );
}

This is the cleanest way to keep icon sizing consistent without repeating a prop on every instance.

TypeScript Tips

Every icon is an IconType, which is a function component taking IconBaseProps. That makes icons easy to pass around as data:

import type { IconType } from 'react-icons';
import { FiHome, FiUsers } from 'react-icons/fi';

type NavItem = { label: string; href: string; icon: IconType };

const nav: NavItem[] = [
  { label: 'Home', href: '/', icon: FiHome },
  { label: 'Team', href: '/team/', icon: FiUsers },
];

Render with <item.icon />. Typing the field as IconType rather than ReactNode keeps the component uninstantiated, so the consumer decides its size.

Common Gotchas

The big one is the barrel import. import { FaGithub } from 'react-icons' looks equivalent to the subpath version and is not; depending on your bundler it can pull far more than one icon. Always import from the set.

The second is editor performance. Thirty sets is a lot of type declarations, and autocomplete in a large project can get sluggish. If you have settled on one family, moving to that family's own package is the fix.

The third is duplicated concepts across sets. Nothing stops you using FaTrash in one component and FiTrash2 in another, and the mismatch is visible. Pick a primary set and treat the others as exceptions.

Frequently Asked Questions

Does installing react-icons put every icon set in my bundle?

No, as long as you import from the set's subpath. `import { FaGithub } from 'react-icons/fa'` pulls that one component; the package is structured so bundlers can drop the rest. What does hurt is a barrel import from 'react-icons' itself, which some bundler configurations fail to shake.

How do I control the size and color of an icon?

Both inherit from CSS by default: the SVG uses `1em` for size and `currentColor` for fill, so an icon matches the font-size and color of its parent. Override per icon with the size and color props, or set them for a subtree with IconContext.Provider.

How is this different from installing an icon set directly?

A single set installed directly is usually the better choice - lucide-react for Lucide, @heroicons/react for Heroicons. React Icons earns its place when you need icons from several families in one product, or when you don't yet know which family you'll settle on and want to avoid rewriting imports later.

Are the icons accessible by default?

They render as SVG with no accessible name, which is correct for decoration next to a text label. When an icon is the only content of a control, give the control the name: put aria-label on the button, or pass a title prop to the icon so it renders a <title> element.