Skip to main content

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​

OperationAsyncStorageMMKV
Read 1KB~5ms~0.01ms
Write 1KB~10ms~0.02ms
Bridge callsYesNo (JSI)
Sync APINoYes

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​

FeatureJS OnlyWith Native Modules
Memory Cacheβœ…βœ…
Disk CacheAsyncStorageMMKV (JSI)
Read Speed~5ms~0.01ms
Write Speed~10ms~0.02ms
Bridge CallsYesNo (JSI)
Sync APINoYes
Shared MemoryNoYes

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​