npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tatil/react-native-app-icon

v0.0.2

Published

Switch your React Native app icon at runtime (seasonal, campaign or user-picked icons) on iOS and Android. Never crashes, no dependencies.

Readme

@tatil/react-native-app-icon

Switch your React Native app icon at runtime: seasonal icons, campaign icons, or an icon the user picks. iOS and Android, no dependencies.

import AppIcon from '@tatil/react-native-app-icon';

await AppIcon.setIcon('christmas'); // 🎄
await AppIcon.setIcon('default'); // back to normal

Named imports work too: import { setIcon } from '@tatil/react-native-app-icon'.

Why another icon package?

Changing the icon should never be the reason your app crashes. This package is built around that:

  • Never throws, never rejects. Every call resolves. Native errors are logged, not thrown.
  • Unknown names are safe. setIcon('does-not-exist'), setIcon(null) or setIcon(undefined) switch to the default icon, so a server can send any value.
  • No pointless switches. Setting the icon that is already active does nothing, so iOS users don't see the "You have changed the icon" alert on every launch.
  • Android-safe timing. Swapping the launcher entry while the app is on screen makes some launchers close the app. On Android the switch is applied when the app goes to the background.
  • Never iconless on Android. The new icon is enabled before the old one is disabled.
  • No hardcoded list. Available icons are read from the app itself: Info.plist on iOS, AndroidManifest.xml on Android.
  • No dependencies. A plain TurboModule: Objective-C++ on iOS, Kotlin on Android.

Requirements

| Dependency | Version | | --- | --- | | react-native | >= 0.76 with the New Architecture (developed against 0.86) | | iOS | 15.1+ | | Android | API 24+ |

Installation

yarn add @tatil/react-native-app-icon
cd ios && bundle exec pod install

Then add your icons on each platform as shown below and rebuild the app. Autolinking registers the native module.

Setup

Pick a short, lowercase name for each extra icon, for example autumn and christmas. Use the same name on iOS, on Android and in JS. The name default is reserved for your current icon.

iOS

  1. In Xcode, open your asset catalog (Images.xcassets) and add a new iOS App Icon set per icon, named exactly after the icon: autumn, christmas.

  2. Drop a 1024×1024 PNG (no transparency) into each set.

  3. In the target's Build Settings, set Alternate App Icon Sets (ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES) to the names, separated by spaces. Do it for both Debug and Release:

    ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = autumn christmas

    Xcode then writes CFBundleAlternateIcons into the built Info.plist, which is where the package finds your icons.

[!NOTE] iOS always shows a system alert after the icon changes. Apple does not let apps hide it.

Android

Android switches icons by turning launcher entries (<activity-alias>) on and off.

  1. Add the icon resources, for example res/mipmap-*/ic_launcher_autumn.png, or an adaptive icon in res/mipmap-anydpi-v26/ic_launcher_autumn.xml.
  2. In android/app/src/main/AndroidManifest.xml, remove the MAIN/LAUNCHER intent filter from MainActivity. Keep its other intent filters, such as deep links.
  3. Add one <activity-alias> per icon, including default. Each alias name must end with .AppIcon.<icon name>, and only default is enabled:
<activity android:name=".MainActivity" ...>
  <!-- MAIN/LAUNCHER intent filter removed from here; deep links stay -->
</activity>

<activity-alias
  android:name=".AppIcon.default"
  android:targetActivity=".MainActivity"
  android:label="@string/app_name"
  android:icon="@mipmap/ic_launcher"
  android:roundIcon="@mipmap/ic_launcher_round"
  android:enabled="true"
  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=".AppIcon.autumn"
  android:targetActivity=".MainActivity"
  android:label="@string/app_name"
  android:icon="@mipmap/ic_launcher_autumn"
  android:roundIcon="@mipmap/ic_launcher_autumn"
  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>

<!-- same for .AppIcon.christmas -->

If there is no .AppIcon.default alias, the package does nothing on Android and logs a warning instead of guessing.

Usage

Icon controlled by your backend

The most common case: your backend decides which icon is live, for example through a ping or config endpoint.

import AppIcon from '@tatil/react-native-app-icon';

const response = await api.ping();

// "christmas" -> christmas icon; "default", unknown or missing -> default icon
AppIcon.setIcon(response.iconName);

You don't need to check whether the icon exists or is already active, and you don't need a try/catch.

Let the user choose

import AppIcon from '@tatil/react-native-app-icon';

const icons = AppIcon.getIcons(); // ['default', 'autumn', 'christmas']
const current = await AppIcon.getIcon(); // 'default'

const onPick = async name => {
  const active = await AppIcon.setIcon(name);
  console.log(`icon is now ${active}`);
};

API

All functions are available on the default export (AppIcon.setIcon) and as named exports (setIcon).

| Function | Returns | Description | | --- | --- | --- | | setIcon(name?) | Promise<string> | Switches to name. Anything that is not a bundled icon switches to default. Resolves with the icon that is (iOS) or will be (Android) shown. Never rejects. | | getIcon() | Promise<string> | The icon currently on the home screen. On Android, a switch still waiting for the app to go to the background is returned as current. | | getIcons() | string[] | Every icon the app ships, default first. | | DEFAULT_ICON | 'default' | The reserved name of your primary icon. |

TypeScript types are included.

Platform notes

iOS

  • The system alert after a change cannot be hidden.
  • iOS refuses the change while the app is not in the foreground. The call then resolves with the icon that is still active, so you can try again on the next launch.

Android

  • The change is applied when the app leaves the foreground. If the app is already in the background, it is applied immediately.
  • Some launchers take a few seconds to show the new icon. On some launchers a pinned home screen shortcut is removed and the user has to add the app again. This is launcher behavior and affects every icon-switching library.
  • The first release that moves the launcher entry from MainActivity to .AppIcon.default changes the launcher component. Before shipping, test the update path (old version installed, then the new one) on the launchers your users have, such as Pixel and Samsung One UI.

Troubleshooting

| Problem | Fix | | --- | --- | | getIcons() returns only ['default'] on iOS | ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES is missing or the asset set names don't match. Clean the build folder and rebuild. | | getIcons() returns only ['default'] on Android | Alias names must end with .AppIcon.<name> and have the MAIN/LAUNCHER intent filter. | | Two icons on the Android home screen | More than one alias has android:enabled="true". Only .AppIcon.default should. | | react-native run-android builds but fails with Activity class {…} does not exist | The CLI looks for the LAUNCHER filter on an <activity> and doesn't see aliases. Run it with --main-activity .MainActivity (add it to your android script). | | native module is not linked warning | Rebuild the native app after installing; run pod install on iOS. |

License

MIT