Font Awesome in React, Vue and Angular
Use the official component packages with the icon library pattern, keep tree-shaking intact, and work around the v7 removal of the dynamic icon component.
React
npm install @fortawesome/react-fontawesome
npm install @fortawesome/fontawesome-svg-core
npm install @fortawesome/free-solid-svg-icons
npm install @fortawesome/free-regular-svg-icons
npm install @fortawesome/free-brands-svg-icons
# Pro projects add the matching pro packages instead// The library pattern: add icons once at the application root, then refer to
// them by name anywhere. Short names are what make this convenient.
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCartShopping, faBell, faUser } from '@fortawesome/free-solid-svg-icons';
library.add(faCartShopping, faBell, faUser);
// ---- Later, in any component -------------------------------------------
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
export function Toolbar() {
return (
<header>
{/* By name, using the library */}
<FontAwesomeIcon icon="bell" />
{/* Or by importing the definition directly: the tree-shakeable path */}
<FontAwesomeIcon icon={faCartShopping} />
{/* Sizing, colour and animation are props, not classes */}
<FontAwesomeIcon icon="user" size="lg" color="#6d28d9" spin />
{/* An icon that is the only content of a button needs a label */}
<button type="button" aria-label="Notifications">
<FontAwesomeIcon icon="bell" aria-hidden="true" />
</button>
</header>
);
}// v7 removed the dynamic "object" icon component that resolved icons by
// string from the whole library (and the dynamic importing feature that
// went with it). The supported patterns now are:
// 1. By string, for icons registered with library.add()
<FontAwesomeIcon icon="bell" />
<FontAwesomeIcon icon={['fas', 'bell']} /> // explicit prefix
<FontAwesomeIcon icon={['far', 'bell']} />
// 2. By imported definition: nothing to register, fully tree-shakeable
import { faBell } from '@fortawesome/free-solid-svg-icons';
<FontAwesomeIcon icon={faBell} />
// 3. A custom icon defined in your own project
import { faMyGlyph } from './icons/faMyGlyph';
<FontAwesomeIcon icon={faMyGlyph} />
// What no longer works: passing a string that was never registered, or
// relying on the component to look up an arbitrary icon at runtime.| Prop | Values | Note |
|---|---|---|
icon | A string, a tuple, or a definition | The only required prop |
size | xs through 2xl | Maps to the sizing scale |
fixedWidth | Boolean | Equal widths for a vertical list |
spin / pulse | Boolean | Built-in animation |
beat / fade / bounce / shake | Boolean | v7 animation props |
rotation / flip | 90, 180, 270 / horizontal, vertical | Power transforms |
transform | A string such as shrink-2 | The SVG transform syntax |
mask | An icon definition | Composites on top of the icon |
listItem | Boolean | For fa-li style lists |
swapOpacity | Boolean | Duotone layer swap |
aria-hidden / title | Boolean / string | Accessibility, exactly as in plain markup |
💡
Adding icons to the library at the root keeps components short but weakens tree-shaking: the icons reachable from the library object are all retained. Importing each icon definition where it is used and passing the definition is the pattern that lets a bundler drop the icons you never reference.
Vue 3
// main.js — the same library pattern
import { createApp } from 'vue';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faBell, faCartShopping } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import App from './App.vue';
library.add(faBell, faCartShopping);
createApp(App)
.component('FontAwesomeIcon', FontAwesomeIcon) // global registration
.mount('#app');<script setup>
// Local import instead of a global component: better for tree-shaking.
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { faBell } from '@fortawesome/free-solid-svg-icons';
const props = defineProps({ unread: { type: Number, default: 0 } });
</script>
<template>
<button type="button" :aria-label="`Notifications, ${props.unread} unread`">
<FontAwesomeIcon :icon="faBell" />
<span v-if="props.unread" class="badge">{{ props.unread }}</span>
</button>
</template>
<!-- Props are identical to React's: size, spin, rotation, transform, mask,
listItem, swapOpacity. Attribute names stay kebab-case in templates. -->// A global component that wraps the size and colour so templates stay clean.
import { h } from 'vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
export const AppIcon = {
props: { icon: { type: [String, Array, Object], required: true }, size: { type: String, default: '1x' } },
setup(props) {
return () => h(FontAwesomeIcon, {
icon: props.icon,
size: props.size,
class: 'app-icon',
'aria-hidden': true
});
}
};
// Used as <AppIcon icon="bell" /> with the styling in one stylesheet.Angular
npm install @fortawesome/angular-fontawesome @fortawesome/fontawesome-svg-core
npm install @fortawesome/free-solid-svg-icons// app.config.ts — register the icons your application uses.
import { ApplicationConfig } from '@angular/core';
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faBell, faCartShopping } from '@fortawesome/free-solid-svg-icons';
import { faGithub } from '@fortawesome/free-brands-svg-icons';
export const appConfig: ApplicationConfig = {
providers: [
{
provide: FaIconLibrary,
useFactory: () => {
const library = new FaIconLibrary();
// register icons by prefix, or add whole icon packs
library.addIcons(faBell, faCartShopping, faGithub);
return library;
}
}
]
};import { Component, input } from '@angular/core';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { faBell } from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'app-notification-button',
imports: [FaIconComponent],
template: `
<button type="button" [attr.aria-label]="label()">
<fa-icon [icon]="bell" [animation]="'beat-fade'" />
@if (unread() > 0) { <span class="badge">{{ unread() }}</span> }
</button>
`
})
export class NotificationButtonComponent {
// Import the definition and pass it in: fully tree-shakeable.
readonly bell = faBell;
readonly unread = input(0);
readonly label = input('Notifications');
}
// Inputs on fa-icon mirror the other frameworks:
// [icon] [size] [fixedWidth] [rotate] [flip] [spin] [pulse] [border]
// [transform] [mask] [inverse] [listItem] [pull] [animation] [stackItemSize]| Framework | Package | Register where | Tree-shaking |
|---|---|---|---|
| React | @fortawesome/react-fontawesome | library.add at the root, or pass a definition | Best with direct definitions |
| Vue 3 | @fortawesome/vue-fontawesome | Global component plus library.add | Best with local imports |
| Angular | @fortawesome/angular-fontawesome | FaIconLibrary provider | Best with direct definitions |
| Svelte | @fortawesome/svelte-fontawesome | Import and pass | Good |
| Astro | Any of the above in the framework island | Per island | Depends on the island |
| Plain HTML | dom.watch() | The library plus a watcher | Poor |
// Custom icons in a typed setup: the definition is a plain object.
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
export const faTelescope: IconDefinition = {
prefix: 'fas',
iconName: 'telescope',
icon: [512, 512, [], 'f8ff', 'M... long path data ...'],
// ^width ^height ^ligatures ^unicode ^path
};
// Register it like any other icon:
// library.add(faTelescope);
// or pass it directly: <FontAwesomeIcon :icon="faTelescope" />
// TypeScript note: the tuple is [width, height, ligatures, unicode, path] for
// a single-path icon, or an array of path strings for a multi-path icon.FAQ
Why is my bundle growing with every icon?
The library pattern registers icons at the root, so every icon reachable from the library object is retained. Import each icon definition at the point of use and pass the definition to the component instead of a string.
What replaced the v6 dynamic icon component?
Nothing equivalent. v7 removed the ability to resolve an arbitrary icon name at runtime, because that requires bundling every icon. Register what you use with the library, or import definitions directly — both are static and bundle-friendly.
Related
SVG with JavaScript: how it works and when to use it Build-time optimisation, subsets and bundle budgets
Last refreshed 2026-09-18.