Skip to main content

Babel Plugin

Bundle icons at build time for 0ms first render - no network requests, no loading states.

info

The Babel plugin is optional. rn-iconify works out of the box with runtime fetching. Use this for production optimization.

Installationโ€‹

The Babel plugin is included with rn-iconify:

npm install rn-iconify

Configurationโ€‹

Add to your babel.config.js:

module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
['rn-iconify/babel', {
// Icons are auto-detected from your source code
// No manual configuration needed!
}],
],
};

How It Worksโ€‹

The Babel plugin automatically:

  1. Scans your code - Finds all icon usages like <Mdi name="home" />
  2. Collects icon names - Tracks which icons are used in each file
  3. Fetches at build time - Downloads SVG data from Iconify API
  4. Generates cache - Creates a local cache file with all icon data
  5. Instant runtime - Icons render immediately without network requests
// Your code - plugin detects these automatically
<Mdi name="home" />
<Heroicons name="user" />
prefetchIcons(['mdi:settings', 'lucide:camera'])

Configuration Optionsโ€‹

module.exports = {
plugins: [
['rn-iconify/babel', {
// Include specific patterns (supports wildcards)
include: [
'mdi:*', // All Material Design Icons
'heroicons:*', // All Heroicons
],

// Exclude patterns
exclude: [
'mdi:test-*', // Skip test icons
],

// Output path for generated cache
outputPath: '.rn-iconify-cache',

// Enable verbose logging
verbose: true,

// Disable the plugin temporarily
disabled: false,
}],
],
};

Options Referenceโ€‹

OptionTypeDefaultDescription
includestring[][]Icon patterns to include (wildcards supported)
excludestring[][]Icon patterns to exclude
outputPathstring.rn-iconify-cacheGenerated cache location
verbosebooleanfalseLog detected icons during build
disabledbooleanfalseDisable the plugin

Pattern Matchingโ€‹

Use glob patterns for flexible icon selection:

plugins: [
['rn-iconify/babel', {
include: [
'mdi:home*', // home, home-outline, home-variant, etc.
'mdi:arrow-*', // All arrow icons
'heroicons:*-solid', // All solid Heroicons
],
exclude: [
'mdi:*-off', // Exclude disabled variants
],
}],
],

Auto-Detectionโ€‹

By default, the plugin auto-detects icons from your code:

// All of these are automatically detected:

// JSX usage
<Mdi name="home" />
<Heroicons name="user" />
<Lucide name="camera" />

// prefetchIcons calls
prefetchIcons(['mdi:home', 'mdi:settings'])

Bundle Size Impactโ€‹

Bundled icons add to your JavaScript bundle:

Icons BundledApproximate Size
10 icons~5 KB
100 icons~50 KB
1,000 icons~500 KB
warning

An icon the plugin does not detect is not missing โ€” it is fetched from the Iconify API at runtime, in release builds too. That is a request on every install, a placeholder until it lands, and nothing at all offline. Run npx rn-iconify doctor to see which icons are in that state.

danger

include and exclude filter what was detected. They never add anything. Setting include: ['mdi:loading'] does not bundle that icon โ€” it discards every icon that does not match, leaving the rest to be fetched at runtime. To add names the plugin cannot see, use defineIcons.

Names Chosen at Runtimeโ€‹

Most icon names are found in your source even when they are not written on the icon component. A component that declares an icon prop names its set in the type, so anything handed to it is detected:

// Detected โ€” the prop type says which set 'person-outline' belongs to
function Row({ icon }: { icon: IonIconName }) {
return <Ion name={icon} />;
}

<Row icon="person-outline" />;

What cannot be detected is a name the source never states as belonging to a set:

// Nothing here says 'hanger' is an Mdi icon
const CATEGORY_ICON: Record<string, string> = { OUTFIT: 'hanger' };

// Nor here โ€” the name does not exist until it runs
const iconName = `mdi:${state}`;

Declare those with defineIcons:

import { defineIcons } from 'rn-iconify';
import type { MdiIconName } from 'rn-iconify';

const CATEGORY_ICON = defineIcons<MdiIconName>({
OUTFIT: 'hanger',
SPOTLIGHT: 'theater',
});

const STATES = defineIcons<MdiIconName>(['loading', 'success', 'error']);

The type argument does two things: it stops a typo reaching a build, and it tells the plugin which set the names belong to.

Typing the map is equally good, and needs no extra call:

const CATEGORY_ICON: Record<string, MdiIconName> = { OUTFIT: 'hanger' };
tip

npx rn-iconify doctor lists every icon still being fetched at runtime, so you can see whether any are left.

Expo Configurationโ€‹

For Expo projects, modify babel.config.js:

module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
['rn-iconify/babel', {
verbose: __DEV__, // Only log in development
}],
],
};
};

Clearing Cacheโ€‹

After changing configuration, clear Metro cache:

# React Native CLI
npx react-native start --reset-cache

# Expo
npx expo start --clear

Build Outputโ€‹

With verbose: true, you'll see detected icons during build:

[rn-iconify] Build started. Project root: /path/to/project
[rn-iconify] Detected 47 unique icons across 23 files
[rn-iconify] Icon sets: mdi (28), heroicons (12), lucide (7)
[rn-iconify] Generating bundle...
[rn-iconify] Bundle generated: .rn-iconify-cache/icons.json (15.2 KB)

Generated Filesโ€‹

The plugin creates a cache directory:

.rn-iconify-cache/
โ”œโ”€โ”€ icons.json # Icon data
โ””โ”€โ”€ icons.js # JavaScript module

Add to .gitignore:

.rn-iconify-cache/

CI/CD Integrationโ€‹

Ensure icons are bundled in CI:

# .github/workflows/build.yml
- name: Build App
run: |
npm install
npx react-native bundle \
--platform android \
--dev false \
--entry-file index.js \
--bundle-output ./android/app/src/main/assets/index.android.bundle

Troubleshootingโ€‹

Icons Not Bundlingโ€‹

  1. Check icon component names are correct (Mdi, not mdi)
  2. Clear Metro cache
  3. Enable verbose mode to see detected icons
['rn-iconify/babel', {
verbose: true,
}]

Network Errors During Buildโ€‹

The plugin needs network access to fetch icons. If behind a proxy:

# Set proxy environment variables
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080

Large Bundle Sizeโ€‹

  1. Review what icons are being bundled (use verbose: true)
  2. Use specific includes instead of wildcards
  3. Add excludes for unused icon variants

Performance Comparisonโ€‹

MethodFirst RenderBundle SizeNetwork
Runtime fetch~100-500msMinimalRequired
Prefetch~0ms (after prefetch)MinimalRequired
Babel plugin0msIncreasesNone
  1. Development: Let plugin auto-detect icons
  2. Production: Review bundled icons, optimize with includes/excludes
  3. Critical path: Ensure navigation and common UI icons are bundled
// babel.config.js
const isProduction = process.env.NODE_ENV === 'production';

module.exports = {
plugins: [
['rn-iconify/babel', {
verbose: !isProduction,
// In production, explicitly include only needed icons
include: isProduction ? [
'mdi:home',
'mdi:settings',
'mdi:account',
'mdi:arrow-left',
] : [],
}],
],
};

Next Stepsโ€‹