Architecture
Understanding how rn-iconify works under the hood.
Overviewβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your React Native App β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β <Mdi name="home" /> β
β β β
β βΌ β
β βββββββββββββββ β
β β Icon Cache β βββ 1. Check memory cache β
β β (Memory) β β
β ββββββββ¬βββββββ β
β β miss β
β βΌ β
β βββββββββββββββ β
β β MMKV Cache β βββ 2. Check persistent cache (JSI) β
β β (Native) β β
β ββββββββ¬βββββββ β
β β miss β
β βΌ β
β βββββββββββββββ βββββββββββββββ β
β β Bundled β ββββ β Babel Pluginβ (build-time) β
β β Icons β βββββββββββββββ β
β ββββββββ¬βββββββ β
β β miss β
β βΌ β
β βββββββββββββββ β
β β Iconify β βββ 3. Fetch from API β
β β API β β
β βββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Loading Pipelineβ
1. Memory Cacheβ
First, check in-memory cache for instant access:
// Internal implementation
const memoryCache = new Map<string, IconData>();
function getIcon(name: string): IconData | null {
return memoryCache.get(name) || null;
}
- Speed: ~0.01ms
- Capacity: Limited by app memory
- Persistence: Lost on app close
2. MMKV Native Cacheβ
If not in memory, check native storage:
// Uses react-native-mmkv via JSI
const storage = new MMKV({ id: 'rn-iconify-cache' });
function getCachedIcon(name: string): IconData | null {
const data = storage.getString(name);
return data ? JSON.parse(data) : null;
}
- Speed: ~0.1ms (via JSI, no bridge overhead)
- Capacity: Configurable (default 10MB)
- Persistence: Survives app restarts
3. Bundled Icons (Babel Plugin)β
Check build-time bundled icons:
// Generated at build time
import { bundledIcons } from './__generated__/icons';
function getBundledIcon(name: string): IconData | null {
return bundledIcons[name] || null;
}
- Speed: 0ms (compiled into bundle)
- Capacity: As configured
- Persistence: Part of app binary
4. Iconify APIβ
Finally, fetch from network:
async function fetchIcon(name: string): Promise<IconData> {
const [prefix, iconName] = name.split(':');
const response = await fetch(
`https://api.iconify.design/${prefix}.json?icons=${iconName}`
);
return response.json();
}
- Speed: 100-500ms
- Capacity: Unlimited (268,000+ icons)
- Persistence: Cached after first fetch
Component Architectureβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β IconComponent β
β (e.g., <Mdi />) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β β
β βΌ β
β ββββββββββββββββββββ β
β β useIconLoader β β
β β (Hook) β β
β ββββββββββ¬ββββββββββ β
β β β
β ββββββββββββββΌβββββββββββββ β
β βΌ βΌ βΌ β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Loading β β SVG β β Error β β
β β State β β Render β β State β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β react-native-svg β β
β β <Svg /> β β
β ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Icon Data Flowβ
// 1. User renders icon
<Mdi name="home" size={24} color="blue" />
// 2. Component extracts props
const { name, size, color, ...rest } = props;
// 3. Hook manages loading state
const { data, loading, error } = useIconLoader('mdi:home');
// 4. Render based on state
if (loading) return <Placeholder />;
if (error) return <ErrorView />;
// 5. Transform SVG data
const svgProps = transformIcon(data, { size, color, ...rest });
// 6. Render with react-native-svg
return <Svg {...svgProps} />;
Type Systemβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Type Generation β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Iconify API β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β Build Script β (postinstall) β
β β generates types β β
β ββββββββββ¬ββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β src/types/ β β
β β mdi.d.ts β type MdiIconName β β
β β heroicons.d.ts β type HeroiconsIconName β β
β β lucide.d.ts β type LucideIconName β β
β β ... β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Each icon set has its own type:
// Generated type (example)
export type MdiIconName =
| 'home'
| 'home-outline'
| 'home-variant'
| 'settings'
| 'account'
// ... 7,447 icons
;
// Component uses this type
interface MdiProps {
name: MdiIconName; // Only valid MDI icon names
size?: number;
color?: string;
}
Why Per-Component Types?β
Single Union Type (Bad) Per-Component Types (Good)
βββββββββββββββββββββ βββββββββββββββββββββββββ
type IconName = type MdiIconName = ... (~7K)
| MdiIcons (~7K) type HeroiconsIconName = ... (~1K)
| Heroicons (~1K) type LucideIconName = ... (~1K)
| Lucide (~1K)
| ... (268K total) Each component: ~7K max
β IDE crashes with 268K union β
IDE stays fast
β TypeScript OOM β
TypeScript works
β Autocomplete takes 30s+ β
Autocomplete instant
MMKV Cache Architectureβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MMKV Storage β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β JavaScript β β
β β β β
β β import { MMKV } from 'react-native-mmkv'; β β
β β const storage = new MMKV(); β β
β β β β
β βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ β
β β β
β β JSI (no bridge) β
β β β
β βββββββββββββββββββββββΌβββββββββββββββββββββββββββββ β
β β C++ Core β β
β β β β
β β β’ Memory-mapped file I/O β β
β β β’ ~30x faster than AsyncStorage β β
β β β’ Synchronous API β β
β β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Performance Comparisonβ
| Operation | AsyncStorage | MMKV |
|---|---|---|
| Read 1KB | ~5ms | ~0.01ms |
| Write 1KB | ~10ms | ~0.02ms |
| Bridge calls | Yes | No (JSI) |
| Sync API | No | Yes |
Babel Plugin Pipelineβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Build Time (Babel) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Source Code β
β βββββββββββββ β
β <Mdi name="home" /> β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β Babel Plugin β β
β β Detects icons β β
β ββββββββββ¬ββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β Fetch from β β
β β Iconify API β β
β ββββββββββ¬ββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β Generate Bundle β β
β β __icons__.js β β
β ββββββββββ¬ββββββββββ β
β β β
β βΌ β
β Transformed Code β
β βββββββββββββββββ β
β <Mdi name="home" __bundled /> β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Animation Systemβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Animation Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β <AnimatedIcon animate="spin"> β
β <Mdi name="loading" /> β
β </AnimatedIcon> β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β useIconAnimation β β
β β Hook β β
β ββββββββββ¬ββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β Animated.Value β β Native driver β
β β (Native Thread) β β
β ββββββββββ¬ββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββ β
β β Animated.View β β
β β transform β β
β ββββββββββββββββββββ β
β β
β Presets: β
β β’ spin: rotate 0Β° β 360Β° (loop) β
β β’ pulse: opacity 1 β 0.4 β 1 (loop) β
β β’ bounce: translateY 0 β -10 β 0 (loop) β
β β’ shake: translateX -5 β 5 β -5 (loop) β
β β’ ping: scale 1 β 1.5 + opacity 1 β 0 (loop) β
β β’ wiggle: rotate -15Β° β 15Β° β -15Β° (loop) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
File Structureβ
rn-iconify/
βββ src/
β βββ components/ # Icon components
β β βββ Mdi.tsx
β β βββ Heroicons.tsx
β β βββ ...
β β
β βββ hooks/ # React hooks
β β βββ useIconLoader.ts
β β βββ useAnimation.ts
β β βββ useIconTheme.ts
β β
β βββ cache/ # Caching layer
β β βββ memory.ts
β β βββ mmkv.ts
β β
β βββ api/ # Iconify API client
β β βββ iconify.ts
β β
β βββ types/ # Generated types
β β βββ mdi.d.ts
β β βββ ...
β β
β βββ index.ts # Public exports
β
βββ babel/ # Babel plugin
β βββ index.js
β
βββ cli/ # CLI tools
β βββ index.js
β
βββ native/ # Native modules
βββ ios/
βββ android/
Performance Optimizationsβ
1. Deduplicationβ
// Multiple icons with same name share data
<Mdi name="home" />
<Mdi name="home" /> // Reuses cached data
<Mdi name="home" /> // Reuses cached data
2. Batched Requestsβ
// Icons requested together are batched
<>
<Mdi name="home" /> // ββ
<Mdi name="settings" /> // ββΌβ Single API request
<Mdi name="account" /> // ββ
</>
3. SVG Optimizationβ
// SVG paths are optimized
const original = "M10.5 20.5v-6h4v6h5v-8h3L12 3 2 12h3v8z";
const optimized = "M10.5 20.5v-6h4v6h5v-8h3L12 3 2 12h3v8z";
// Paths are already optimized by Iconify
Native Modulesβ
rn-iconify includes optional native modules for enhanced performance.
TurboModules Supportβ
When React Native's new architecture is enabled, rn-iconify uses TurboModules:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TurboModules Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β JavaScript Native (C++/ObjC/Java) β
β ββββββββββ ββββββββββββββββββββββ β
β β
β getIcon('mdi:home') ββββββββββββΊ NativeIconModule β
β β β β
β β βΌ β
β β ββββββββββββββββ β
β β β Memory-mappedβ β
β β β Storage β β
β β ββββββββ¬ββββββββ β
β β β β
β ββββββββββββ IconData βββββββββββββ β
β β
β β’ No serialization overhead β
β β’ Synchronous access via JSI β
β β’ Shared memory between JS and Native β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Native Module Accessβ
The library provides functions to check and access native modules:
import { isNativeModuleAvailable, getNativeIconify } from 'rn-iconify';
// Check if native module is available
if (isNativeModuleAvailable()) {
// Get the native module instance
const nativeModule = getNativeIconify();
// Use native storage (faster)
} else {
// Fall back to JS implementation
}
APIβ
// Check if native MMKV module is available
function isNativeModuleAvailable(): boolean;
// Get the native module instance (returns TurboModule, Bridge module, or fallback)
function getNativeIconify(): NativeIconifyInterface;
Architecture Comparisonβ
| Feature | JS Only | With Native Modules |
|---|---|---|
| Memory Cache | β | β |
| Disk Cache | AsyncStorage | MMKV (JSI) |
| Read Speed | ~5ms | ~0.01ms |
| Write Speed | ~10ms | ~0.02ms |
| Bridge Calls | Yes | No (JSI) |
| Sync API | No | Yes |
| Shared Memory | No | Yes |
Cache Layers Detailβ
Layer 1: Memory Cacheβ
// In-memory Map for instant access
class MemoryCache {
private cache = new Map<string, IconData>();
private maxSize: number;
private accessOrder: string[] = [];
get(key: string): IconData | null {
const data = this.cache.get(key);
if (data) {
// Move to end of access order (LRU)
this.updateAccessOrder(key);
}
return data || null;
}
set(key: string, data: IconData): void {
if (this.cache.size >= this.maxSize) {
// Evict least recently used
this.evictLRU();
}
this.cache.set(key, data);
this.updateAccessOrder(key);
}
}
Layer 2: Native/Disk Cacheβ
// MMKV storage via JSI (when available)
class NativeCache {
private storage: MMKV;
constructor() {
this.storage = new MMKV({
id: 'rn-iconify-cache',
encryptionKey: undefined, // No encryption for speed
});
}
get(key: string): IconData | null {
const data = this.storage.getString(key);
return data ? JSON.parse(data) : null;
}
set(key: string, data: IconData): void {
this.storage.set(key, JSON.stringify(data));
}
}
Layer 3: Bundled Iconsβ
// Build-time generated bundle
// __generated__/icons.js
export const bundledIcons = {
'mdi:home': { body: '<path d="..."/>', width: 24, height: 24 },
'mdi:settings': { body: '<path d="..."/>', width: 24, height: 24 },
// ...
};
// Runtime access (0ms - compiled into bundle)
class BundledCache {
get(key: string): IconData | null {
return bundledIcons[key] || null;
}
}
Layer 4: Network (Iconify API)β
// Network fetching with batching
class NetworkFetcher {
private pendingRequests = new Map<string, Promise<IconData>>();
private batchQueue: string[] = [];
private batchTimeout: NodeJS.Timeout | null = null;
async fetch(name: string): Promise<IconData> {
// Check for existing request (deduplication)
if (this.pendingRequests.has(name)) {
return this.pendingRequests.get(name)!;
}
// Add to batch queue
this.batchQueue.push(name);
// Create promise for this request
const promise = this.scheduleBatch();
this.pendingRequests.set(name, promise);
return promise;
}
private async scheduleBatch(): Promise<IconData> {
// Wait for more requests to batch
await new Promise(resolve => setTimeout(resolve, 10));
// Execute batch request
const icons = this.batchQueue.splice(0);
const response = await this.executeBatch(icons);
// Clear pending requests
icons.forEach(icon => this.pendingRequests.delete(icon));
return response;
}
}
Configuration Systemβ
ConfigManagerβ
Central configuration management:
import { ConfigManager } from 'rn-iconify';
// Get current configuration
const config = ConfigManager.getAPIConfig();
console.log(config.apiUrl); // 'https://api.iconify.design'
// Update configuration
ConfigManager.setConfig({
api: {
apiUrl: 'https://my-iconify-server.com',
timeout: 10000,
},
cache: {
maxMemoryItems: 500,
enableDiskCache: true,
},
});
// Subscribe to config changes
const unsubscribe = ConfigManager.onConfigChange((newConfig) => {
console.log('Config updated:', newConfig);
});
// Reset to defaults
ConfigManager.resetConfig();
Configuration Schemaβ
interface IconifyConfig {
api: {
/** Iconify API URL */
apiUrl: string;
/** Request timeout (ms) */
timeout: number;
/** Retry count */
retries: number;
/** Custom headers */
headers: Record<string, string>;
};
cache: {
/** Max icons in memory */
maxMemoryItems: number;
/** Enable disk cache (MMKV) */
enableDiskCache: boolean;
/** Disk cache key prefix */
diskCachePrefix: string;
};
performance: {
/** Enable monitoring */
enabled: boolean;
/** Track individual icon load times */
trackLoadTimes: boolean;
/** Track cache hit/miss rates */
trackCacheStats: boolean;
/** Maximum entries in history */
maxHistorySize: number;
};
}
Error Handlingβ
Error Recoveryβ
import { configure, fetchIcon } from 'rn-iconify';
// Configure retry behavior globally
configure({
api: {
retries: 3,
retryDelay: 1000,
},
});
// fetchIcon uses configured retry settings automatically
try {
const icon = await fetchIcon('mdi:home');
} catch (error) {
console.error('Failed to fetch icon after retries:', error.message);
}
Next Stepsβ
- Babel Plugin - Build-time bundling
- CLI Tools - Command-line utilities
- Cache Management - Cache configuration
- API Reference - Type definitions