react-native-alternate-app-icon is a high-performance App Icon Changer for React Native that allows you to dynamically change your app's icon at runtime on iOS and Android. Built with Nitro Modules, it provides a type-safe JavaScript API backed by native Swift and Kotlin implementations.
Use it as a dynamic app icon changer for seasonal themes, event-based branding, user personalization, or any scenario where you want to switch icons without shipping a new build.
📖 Full documentation — guides, API reference, and platform setup.
Install the agent skill so coding agents follow correct iOS/Android setup and API usage:
npx skills add rutvik24/react-native-alternate-app-icon -g -yDocs: For AI agents · Install skill (per-provider Add to Cursor and other agents)
- Alternate App Icon Management — Change your app icon at runtime on iOS and Android.
- Cross-Platform API — Same JavaScript functions work on both platforms.
- Nitro-Powered — Fast native bindings via react-native-nitro-modules.
- New Architecture Ready — Works with React Native's New Architecture (Fabric & TurboModules).
- Event-Based Switching — Change icons based on holidays, promotions, user preferences, and more.
| Requirement | Version |
|---|---|
react-native-alternate-app-icon |
0.8.0+ (latest recommended) |
react-native-nitro-modules |
0.32.0 (peer dependency; pin this version) |
| React Native | 0.76.0 or higher |
| Node.js | 18.0.0 or higher |
Native bindings in this library are generated with Nitrogen 0.32.0 for the Nitro Modules 0.32.x Android/iOS APIs. Use a matching react-native-nitro-modules version in your app—mixing Nitro 0.35+ with a build targeting 0.32.x can cause native compile errors.
See the compatibility guide for details.
Install the package along with its peer dependency (pin Nitro to 0.32.0):
bun add react-native-alternate-app-icon react-native-nitro-modules@0.32.0For iOS, install CocoaPods dependencies:
bundle exec pod install --project-directory="ios" Add your primary and alternative icons inside Images.xcassets:
Images.xcassets
├── AppIcon.appiconset
│ ├── Contents.json
│ └── ... (icon images)
└── AlternativeIcon.appiconset
├── Contents.json
└── ... (icon images)
Each alternative icon needs its own .appiconset folder. The folder name (e.g. AlternativeIcon) is the name you pass to setIcon().
Tip: You can use Xcode's asset catalog to generate all required sizes, or provide a single 1024×1024 universal icon:
{
"images": [
{
"filename": "alternate.png",
"idiom": "universal",
"platform": "ios",
"size": "1024x1024"
}
],
"info": {
"author": "xcode",
"version": 1
}
}Add CFBundleIcons to define your primary and alternate icons:
<key>CFBundleIcons</key>
<dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>AppIcon</string>
</array>
</dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>AlternativeIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>AlternativeIcon</string>
</array>
</dict>
</dict>
</dict>- Primary icon: The key under
CFBundleIconFilesmust match your primary.appiconsetname (e.g.AppIcon). - Alternative icons: Add one entry per alternate icon under
CFBundleAlternateIcons. The key (e.g.AlternativeIcon) is the name passed tosetIcon().
- Open your project in Xcode.
- Go to General → App Icons and Launch Screen.
- Set App Icon to your default icon set (e.g.
AppIcon). - Under App Icons Source, enable Include all app icon assets.
The Include all app icon assets checkbox bundles every icon set from your asset catalog so alternate icons are available at runtime.
Android uses activity-alias entries in AndroidManifest.xml. Each alias maps to a launcher icon and corresponds to an icon name used in JavaScript.
Place launcher icons in your mipmap-* resource folders. Use consistent naming across all density folders:
app/src/main/res/
├── mipmap-mdpi/
│ ├── ic_launcher_alternate.png
│ └── ic_launcher_alternate_round.png
├── mipmap-hdpi/
│ ├── ic_launcher_alternate.png
│ └── ic_launcher_alternate_round.png
├── mipmap-xhdpi/
│ ├── ic_launcher_alternate.png
│ └── ic_launcher_alternate_round.png
├── mipmap-xxhdpi/
│ ├── ic_launcher_alternate.png
│ └── ic_launcher_alternate_round.png
├── mipmap-xxxhdpi/
│ ├── ic_launcher_alternate.png
│ └── ic_launcher_alternate_round.png
└── mipmap-anydpi-v26/
├── ic_launcher_alternate.xml
└── ic_launcher_alternate_round.xml
Tip: Use Android Studio's Image Asset Studio (File → New → Image Asset) to generate all required sizes.
Add activity-alias entries for each icon variant. The suffix after MainActivity in the alias name becomes the icon name in JavaScript:
| Alias name | Icon name for setIcon() |
|---|---|
.MainActivityDefault |
"Default" |
.MainActivityAlternativeIcon |
"AlternativeIcon" |
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
...>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name="${applicationId}.MainActivityDefault"
android:targetActivity=".MainActivity"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name="${applicationId}.MainActivityAlternativeIcon"
android:targetActivity=".MainActivity"
android:icon="@mipmap/ic_launcher_alternate"
android:roundIcon="@mipmap/ic_launcher_alternate_round"
android:label="@string/app_name"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</application>Note: On Android, icon changes are applied when the app moves to the background. The library handles this automatically and defers the switch if OAuth or other overlay activities are active.
The library ships consumer ProGuard rules in its AAR (android/consumer-rules.pro). When you enable minifyEnabled true for release, those rules merge automatically — you do not need to copy Nitro keep rules into your app.
Use proguard-android-optimize.txt and avoid redundant -keep rules for Activity or activity-alias (manifest components are kept by R8).
Full guide: ProGuard / R8
Import the functions you need:
import {
getActiveIcon,
setIcon,
getAllAlternativeIcons,
resetIcon,
} from 'react-native-alternate-app-icon'You can also access the underlying Nitro hybrid object directly:
import { AlternateAppIcon } from 'react-native-alternate-app-icon'Sets the app icon to the specified name. Pass "Default" to revert to the primary icon.
await setIcon('AlternativeIcon')
// => "Icon changed to AlternativeIcon" (iOS)
// => "Your icon will change to AlternativeIcon" (Android — applied on background)
await setIcon('Default')Returns the name of the currently active icon. Returns "Default" when the primary icon is active.
const activeIcon = await getActiveIcon()
console.log('Current active icon:', activeIcon)Returns all available icon names configured in your project.
const icons = await getAllAlternativeIcons()
console.log('Available icons:', icons)
// => ["Default", "AlternativeIcon"]Resets the app icon to the primary/default icon.
const message = await resetIcon()
console.log(message)
// => "Icon reset to default."import React, { useEffect, useState } from 'react'
import { Button, Text, View } from 'react-native'
import {
getActiveIcon,
getAllAlternativeIcons,
resetIcon,
setIcon,
} from 'react-native-alternate-app-icon'
export default function App() {
const [activeIcon, setActiveIcon] = useState('')
const [icons, setIcons] = useState([])
useEffect(() => {
;(async () => {
setActiveIcon(await getActiveIcon())
setIcons(await getAllAlternativeIcons())
})()
}, [])
return (
<View>
<Text>Active icon: {activeIcon}</Text>
{icons.map((icon) => (
<Button
key={icon}
title={`Set ${icon}`}
onPress={() => setIcon(icon)}
/>
))}
<Button title="Reset icon" onPress={resetIcon} />
</View>
)
}See the example app for a complete working project.
- Alternate icons require a physical device or simulator running iOS 10.3+.
- iOS shows a system alert when the icon changes — this is expected behavior.
- The icon name must match a key in
CFBundleAlternateIcons, or use"Default"for the primary icon.
- Icon names are derived from
activity-aliasnames: everything afterMainActivityin the alias class name. - The icon switch is deferred until the app is backgrounded for a smoother UX.
- The library automatically waits if sign-in, OAuth, or other overlay activities are in the foreground.
Contributions are welcome! Please read:
- Contributing guide — setup, commits, and pull requests
- Code of Conduct — community standards
- Open an issue — bug, feature, or docs templates
For major changes, open an issue first so we can discuss the approach.