A grid of SVG icons on a monitor with a large red Maine Coon cat resting on a shelf nearby.

React Font Awesome: 30,000 Icons, One Tidy Component

The Orange Cat
The Orange Cat

If you have built anything on the web in the last decade, you have almost certainly run into Font Awesome. It is the icon library behind countless buttons, navbars, and empty states. But the classic approach of loading a giant webfont and sprinkling <i className="fas fa-coffee" /> everywhere fits awkwardly into a modern React app: you ship glyphs you never use, you get no type safety on those magic class strings, and a DOM-scanning script has to swap <i> tags for SVG after render, which is fussy with hydration and server rendering.

@fortawesome/react-fontawesome is the official answer to all of that. It is a small, focused component that ties a Font Awesome icon to a JSX element, renders it as real inline SVG, and lets your bundler tree-shake away every icon you do not import. You get the full 30,000+ icon catalog, including brand logos, with type-checked imports and SSR-friendly output. This article walks through how it fits together and how to use its quieter superpowers.

A Library Built From Three Pieces

The first thing to understand is that React Font Awesome is deliberately not a self-contained icon set. It is the glue between three packages, and you need all three for anything to render:

  1. @fortawesome/fontawesome-svg-core — the rendering engine. It turns an icon definition into an SVG node and handles transforms, masks, layers, and the optional global library.
  2. An icon package — the actual icon data, shipped separately so you only bundle what you use. The free families are @fortawesome/free-solid-svg-icons, @fortawesome/free-regular-svg-icons, and @fortawesome/free-brands-svg-icons.
  3. @fortawesome/react-fontawesome — the React component itself, which connects an icon to a JSX element.

This split is the whole point. Because icons live in separate packages and are imported as plain JavaScript values, a bundler can drop everything you do not touch. It is what makes a 30,000-icon catalog usable in React without shipping all 30,000 icons.

Version 3.x requires React 18 or 19 and Font Awesome SVG core v6 or v7. The component is tiny; the real weight lives in core and the icon packages.

Getting It Onto Your Machine

Remember to install all three pieces, not just the component. This is the single most common stumbling block for newcomers.

With npm:

npm install @fortawesome/react-fontawesome @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons

With yarn:

yarn add @fortawesome/react-fontawesome @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons

The free packages need no extra configuration. Pro packages (@fortawesome/pro-*) and kits (@awesome.me/kit-*) live behind an authenticated npm registry, so they require an .npmrc token — which also matters in CI, where builds will fail without it.

Your First Icon, Imported Like a Value

The recommended path is to import each icon you need and hand it to the component as a prop. This is the tree-shakeable, type-safe approach.

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCoffee } from "@fortawesome/free-solid-svg-icons";

export function CoffeeButton() {
  return (
    <button>
      <FontAwesomeIcon icon={faCoffee} /> Brew
    </button>
  );
}

Because faCoffee is a concrete imported value, only that one icon's data ends up in your bundle. Everything else in the solid pack is dropped. You also get autocomplete and a compile-time error if you misspell the import, which the old class-string approach could never offer.

One important warning: resist the urge to write import * as Icons from "@fortawesome/free-solid-svg-icons" or library.add(fas). Pulling in an entire family drags thousands of icons into your bundle and quietly defeats tree-shaking. Always import individual icons.

Reaching For Icons By Name

For large apps, importing every icon at every use site can get tedious. The alternative is to register icons once into a global library and then reference them by string name anywhere.

// fontawesome.ts — run once at app startup
import { library } from "@fortawesome/fontawesome-svg-core";
import { faCoffee, faHouse } from "@fortawesome/free-solid-svg-icons";
import { faTwitter } from "@fortawesome/free-brands-svg-icons";

library.add(faCoffee, faHouse, faTwitter);
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

// shorthand string (defaults to the solid 'fas' prefix)
<FontAwesomeIcon icon="coffee" />
// explicit prefix + name tuple
<FontAwesomeIcon icon={["fab", "twitter"]} />
// Font Awesome 6 syntax string
<FontAwesomeIcon icon="fa-solid fa-house" />

You still only register the icons you import, so the library can stay reasonably lean. The trade-off is that you lose the type safety of passing an icon object, and a typo'd name fails silently — it renders nothing rather than a build error. For that reason, the explicit-import approach above is usually the safer default, with the library reserved for cases where the convenience genuinely pays off.

Styling Without Touching CSS Files

This is where the component starts to feel like more than a thin wrapper. Most visual tweaks are just props.

Sizing uses both t-shirt sizes (2xs through 2xl) and relative steps (1x through 10x), all relative to the surrounding font size:

<FontAwesomeIcon icon={faCoffee} size="xs" />
<FontAwesomeIcon icon={faCoffee} size="6x" />

Icons inherit color from CSS by default, since they paint with fill: currentColor, so they pick up your text color for free. You can override it inline, and the inverse prop renders white for use inside badges:

<FontAwesomeIcon icon={faCoffee} style={{ color: "tomato" }} />
<FontAwesomeIcon icon={faCheck} inverse />

Rotation, flipping, and a generous set of built-in animations round things out. The animation props include spin, spinPulse, pulse, beat, beatFade, bounce, fade, flip, and shake, all tunable through --fa-* CSS custom properties:

<FontAwesomeIcon icon={faCoffee} rotation={90} />
<FontAwesomeIcon icon={faCoffee} flip="horizontal" />
<FontAwesomeIcon icon={faSpinner} spinPulse />
<FontAwesomeIcon icon={faHeart} beat />

A spinning loader has rarely been this little code.

Power Transforms and Masking

Beyond simple styling, Font Awesome ships a composable transform engine exposed through the transform prop. You can grow, shrink, nudge, and rotate an icon relative to its own coordinate space, as a string or an object:

<FontAwesomeIcon icon={faCoffee} transform="shrink-6 left-4" />
<FontAwesomeIcon icon={faCoffee} transform={{ rotate: 42 }} />

Masking takes this further by cutting one icon out of another, which is perfect for badge-style compositions where the inner icon should be a transparent cutout:

<FontAwesomeIcon
  icon={faCoffee}
  mask={["far", "circle"]}
  transform="shrink-6"
/>

Here the coffee cup is punched out of a regular-style circle, with a shrink applied so it sits neatly inside. This kind of effect would take real SVG plumbing to build by hand; here it is two props.

Stacking Icons Into Layers

When you need to stack multiple icons — a count badge over a circle, a checkmark over a shape — Font Awesome's layering does the heavy lifting. The className approach has always worked, and version 3.1.0 added a dedicated FontAwesomeLayers component for a more idiomatic feel:

<span className="fa-layers fa-fw">
  <FontAwesomeIcon icon={faCircle} />
  <FontAwesomeIcon icon={faCheck} transform="shrink-6" inverse />
</span>

Pair layering with masking and transforms and you have a small composition system for icons, all declared inline in JSX rather than assembled in a separate design tool.

For Pro subscribers there is also duotone, which renders two independently controllable layers through CSS variables like --fa-primary-color and --fa-secondary-color, with a swapOpacity prop to flip the emphasis:

import { faCamera } from "@fortawesome/pro-duotone-svg-icons";

<FontAwesomeIcon
  icon={faCamera}
  swapOpacity
  style={{ "--fa-primary-color": "navy", "--fa-secondary-color": "gold" }}
/>

Accessibility and Server Rendering

Because the component returns a normal React element instead of mutating the DOM after render, it behaves well under SSR and hydration. The title prop adds an SVG <title> for screen readers, and the core engine generates stable ids so that server and client markup match. Version 3.0.0 specifically tightened up id generation to keep hydration consistent.

<FontAwesomeIcon icon={faCoffee} title="Order coffee" />

If an icon is purely decorative, leaving the title off lets the component mark it aria-hidden so assistive technology can skip it.

Where It Earns Its Keep

React Font Awesome is the official, actively maintained binding from the Font Awesome team, and it shows: the issue tracker stays nearly empty and releases land on a steady cadence. Version 3 was a full TypeScript rewrite with a roughly 70% faster internal converter, and recent releases have added custom gradient fills, a custom CSS prefix option, and exported types.

It is not always the lightest choice. If you only need a dozen clean UI glyphs and care most about minimum bytes, a leaner set like Lucide or Heroicons may win on per-icon size. Font Awesome's advantage is breadth and features, not minimum footprint.

Reach for React Font Awesome when you want the official Font Awesome experience: an enormous catalog that includes brand logos and multiple weights of the same icon, a genuine styling engine of sizing, animations, power transforms, masking, layering, and duotone, all expressed as declarative React props — and especially when you or your design team already pay for Font Awesome Pro and want those styles in React. It turns a sprawling, font-based legacy into something that feels native to your component tree, and that is a trade well worth making.