Skip to main content
React Icons is designed for optimal bundle size by leveraging ES6 module imports and tree-shaking. Only the icons you import are included in your final bundle.

What is Tree-Shaking?

Tree-shaking is a dead code elimination technique used by modern JavaScript bundlers (Webpack, Rollup, Vite, etc.) to remove unused exports from your final bundle.
With React Icons, importing 10 icons from a pack of 5,000+ icons will only include those 10 icons in your bundle, not the entire pack.

How It Works

React Icons uses ES6 named exports, which allow bundlers to determine exactly which icons are used:
// Only FaBeer and FaHome are included in your bundle
import { FaBeer, FaHome } from 'react-icons/fa';

function App() {
  return (
    <div>
      <FaBeer />
      <FaHome />
    </div>
  );
}

Behind the Scenes

When you import from react-icons/fa, the bundler:
  1. Analyzes which icons are actually used in your code
  2. Includes only those icon components in the bundle
  3. Excludes all unused icons from the final output
  4. Optimizes the included icons during minification

Bundle Size Impact

Here’s a comparison of bundle sizes:
ScenarioIcons ImportedApproximate Size
No icons0~0 KB
Single icon1~2-3 KB
Ten icons10~20-30 KB
One hundred icons100~200-300 KB
NOT entire Font Awesome pack-NOT ~2+ MB
Actual sizes vary based on the specific icons and your bundler configuration. These are approximate gzipped sizes.

ES6 Import Pattern

React Icons v3+ uses ES6 named imports for optimal tree-shaking:

Package Configuration

React Icons is configured for optimal tree-shaking:
// package.json
{
  "name": "react-icons",
  "module": "lib/index.mjs",    // ES6 module entry point
  "main": "lib/index.js",       // CommonJS entry point
  "sideEffects": false,          // Enables aggressive tree-shaking
  "type": "module"
}

sideEffects: false

The "sideEffects": false declaration tells bundlers that:
  • No code in this package has side effects
  • Unused exports can be safely removed
  • Aggressive tree-shaking is safe
This configuration is already set in React Icons - you don’t need to configure anything!

Verifying Tree-Shaking

Using Webpack Bundle Analyzer

Install and configure webpack-bundle-analyzer:
npm install --save-dev webpack-bundle-analyzer
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};
Run your build to see which icons are included in your bundle.

Using Rollup Plugin Visualizer

For Vite or Rollup projects:
npm install --save-dev rollup-plugin-visualizer
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
    })
  ]
};

Manual Bundle Size Check

# Build your project
npm run build

# Check the output size
ls -lh dist/

Common Tree-Shaking Issues

Issue 1: Large Bundle Size

If your bundle is unexpectedly large:
Problem: Using barrel imports or re-exporting icons
// Avoid: This may prevent tree-shaking
import * as Icons from 'react-icons/fa';

function App() {
  return <Icons.FaBeer />;
}
Solution: Use named imports
// Good: Enables proper tree-shaking
import { FaBeer } from 'react-icons/fa';

function App() {
  return <FaBeer />;
}

Issue 2: Dynamic Icon Loading

Problem: Dynamic imports can include unnecessary icons
// Avoid: May include all icons
import * as Icons from 'react-icons/fa';

function DynamicIcon({ name }) {
  const Icon = Icons[name];
  return <Icon />;
}
Solution: Create a explicit icon map
// Good: Only includes specified icons
import { FaBeer, FaHome, FaUser } from 'react-icons/fa';

const iconMap = {
  beer: FaBeer,
  home: FaHome,
  user: FaUser,
};

function DynamicIcon({ name }) {
  const Icon = iconMap[name];
  return Icon ? <Icon /> : null;
}

Issue 3: Re-exporting Icons

Problem: Re-exporting all icons from a module
// icons.js - Avoid
export * from 'react-icons/fa';
export * from 'react-icons/md';
Solution: Re-export only what you need
// icons.js - Good
export { FaBeer, FaHome } from 'react-icons/fa';
export { MdEmail, MdPhone } from 'react-icons/md';

Best Practices

1. Import Only What You Use

// Good: Specific imports
import { FaBeer, FaHome } from 'react-icons/fa';

// Avoid: Namespace imports
import * as FaIcons from 'react-icons/fa';

2. Group Imports by Pack

// Good: Organized and tree-shakeable
import { 
  FaHome, 
  FaUser, 
  FaCog,
  FaBell 
} from 'react-icons/fa';

import { 
  MdEmail, 
  MdPhone 
} from 'react-icons/md';

3. Use Explicit Icon Maps

For dynamic icon selection:
import { FaHome, FaUser, FaCog } from 'react-icons/fa';

// Explicit map enables tree-shaking
const ICONS = {
  home: FaHome,
  user: FaUser,
  settings: FaCog,
};

function Icon({ name, ...props }) {
  const IconComponent = ICONS[name];
  return IconComponent ? <IconComponent {...props} /> : null;
}

4. Avoid Conditional Imports

// Avoid: Both icons included in bundle
import { FaSun, FaMoon } from 'react-icons/fa';

function ThemeIcon({ isDark }) {
  return isDark ? <FaMoon /> : <FaSun />;
}
// Both FaSun and FaMoon are in the bundle

// This is actually fine! Both icons are imported intentionally.
// Tree-shaking removes unused icons, not conditionally rendered ones.

5. Code Splitting for Large Icon Sets

For pages with many icons, use dynamic imports:
import { lazy, Suspense } from 'react';

// Split icon-heavy components into separate chunks
const IconGallery = lazy(() => import('./IconGallery'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <IconGallery />
    </Suspense>
  );
}

Bundler Compatibility

React Icons works with all modern bundlers that support ES6 modules:
BundlerTree-Shaking SupportConfiguration Required
Webpack 5✅ YesNo (automatic)
Webpack 4✅ YesMode: production
Rollup✅ YesNo (automatic)
Vite✅ YesNo (automatic)
Parcel 2✅ YesNo (automatic)
esbuild✅ YesNo (automatic)
Turbopack✅ YesNo (automatic)
Most modern bundlers enable tree-shaking automatically in production mode.

Measuring Impact

Before and After Example

// Before: Inefficient imports
import * as Icons from 'react-icons/fa';

function App() {
  return (
    <div>
      <Icons.FaBeer />
      <Icons.FaHome />
    </div>
  );
}
// Bundle size: ~2MB (entire Font Awesome pack)
// After: Optimized imports
import { FaBeer, FaHome } from 'react-icons/fa';

function App() {
  return (
    <div>
      <FaBeer />
      <FaHome />
    </div>
  );
}
// Bundle size: ~4KB (just two icons)
// Savings: ~99.8% reduction! 🎉

Alternative: Per-Icon Imports

For specific build systems (Meteor, older Gatsby), use @react-icons/all-files:
import { FaBeer } from '@react-icons/all-files/fa/FaBeer';
import { FaHome } from '@react-icons/all-files/fa/FaHome';

function App() {
  return (
    <div>
      <FaBeer />
      <FaHome />
    </div>
  );
}
This approach is not recommended for most projects. Use standard imports with tree-shaking instead.

Debugging Tree-Shaking

Enable Webpack Stats

// webpack.config.js
module.exports = {
  optimization: {
    usedExports: true, // Enable tree-shaking
  },
  stats: {
    usedExports: true, // Show which exports are used
  },
};

Check Bundle Contents

# View bundled files
ls -lh dist/

# Search for icon references
grep -r "FaBeer" dist/

Performance Tips

  1. Use production builds: Tree-shaking is most effective in production mode
  2. Enable minification: Minifiers remove dead code after tree-shaking
  3. Use code splitting: Split icon-heavy components into separate chunks
  4. Lazy load icon packs: Use dynamic imports for rarely-used icons
  5. Monitor bundle size: Use tools like Bundle Analyzer regularly

Icon Imports

Learn the import patterns that enable tree-shaking

TypeScript

See type-safe imports and tree-shaking