Babel Plugin
Bundle icons at build time for 0ms first render - no network requests, no loading states.
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:
- Scans your code - Finds all icon usages like
<Mdi name="home" /> - Collects icon names - Tracks which icons are used in each file
- Fetches at build time - Downloads SVG data from Iconify API
- Generates cache - Creates a local cache file with all icon data
- 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โ
| Option | Type | Default | Description |
|---|---|---|---|
include | string[] | [] | Icon patterns to include (wildcards supported) |
exclude | string[] | [] | Icon patterns to exclude |
outputPath | string | .rn-iconify-cache | Generated cache location |
verbose | boolean | false | Log detected icons during build |
disabled | boolean | false | Disable 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 Bundled | Approximate Size |
|---|---|
| 10 icons | ~5 KB |
| 100 icons | ~50 KB |
| 1,000 icons | ~500 KB |
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.
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' };
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โ
- Check icon component names are correct (
Mdi, notmdi) - Clear Metro cache
- 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โ
- Review what icons are being bundled (use
verbose: true) - Use specific includes instead of wildcards
- Add excludes for unused icon variants
Performance Comparisonโ
| Method | First Render | Bundle Size | Network |
|---|---|---|---|
| Runtime fetch | ~100-500ms | Minimal | Required |
| Prefetch | ~0ms (after prefetch) | Minimal | Required |
| Babel plugin | 0ms | Increases | None |
Recommended Strategyโ
- Development: Let plugin auto-detect icons
- Production: Review bundled icons, optimize with includes/excludes
- 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โ
- CLI Tools - Analyze icon usage
- Offline Bundles - Full offline support
- Architecture - How bundling works