Skip to content

About

React Native package for iOS UIApplicationShortcutItem and Android App Shortcuts

Resources

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

react-native-app-shortcuts

React Native package for iOS UIApplicationShortcutItem (Quick Actions) and Android App Shortcuts.

This package lets you add home screen shortcuts to your React Native app, so users can jump straight to a specific action from the home screen.

Features

  • Dynamic shortcuts on iOS and Android
  • Handles both cold launch and warm taps
  • SF Symbols on iOS
  • Round-trips arbitrary extra data (userInfo) on both platforms
  • TypeScript types
  • New Architecture (TurboModules) with codegen; Old Architecture still supported

Requirements

Minimum
React Native 0.76
iOS 15.1
Android API 24 (shortcuts themselves need API 25 / Android 7.1 — see isSupported())

Verified against React Native 0.85.3.

Installation

npm install @mbdayo/react-native-app-shortcuts

or

yarn add @mbdayo/react-native-app-shortcuts

iOS Setup

cd ios && pod install

Then forward the tapped shortcut from your AppDelegate. React Native 0.77+ ships AppDelegate.swift by default, so that is covered first.

AppDelegate.swift (React Native 0.77+ default)

Add a bridging header — create ios/<YourApp>/<YourApp>-Bridging-Header.h if you do not already have one:

#import <RNAppShortcuts/RNAppShortcuts.h>

and point Xcode at it: select your target → Build Settings → Objective-C Bridging Header → <YourApp>/<YourApp>-Bridging-Header.h.

Then add this method to your AppDelegate class:

func application(
  _ application: UIApplication,
  performActionFor shortcutItem: UIApplicationShortcutItem,
  completionHandler: @escaping (Bool) -> Void
) {
  RNAppShortcuts.handle(shortcutItem)
  completionHandler(true)
}

The RN 0.77+ template's AppDelegate conforms to UIApplicationDelegate directly rather than subclassing, so this is a plain func, not an override func.

handle(_:) is how Swift imports the Objective-C +handleShortcutItem: — the importer prunes the type-name suffix, the same way performActionForShortcutItem: becomes performActionFor shortcutItem:.

AppDelegate.mm / AppDelegate.m

Add the import at the top of the file:

#import <RNAppShortcuts/RNAppShortcuts.h>

and add this method to your @implementation AppDelegate:

- (void)application:(UIApplication *)application
    performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem
               completionHandler:(void (^)(BOOL))completionHandler {
  [RNAppShortcuts handleShortcutItem:shortcutItem];
  completionHandler(YES);
}

Android Setup

No additional setup required.

New Architecture

The New Architecture is enabled by default from React Native 0.76 and is fully supported, including codegen — the module extends the generated spec on both platforms. No configuration is needed.

The Old Architecture is still supported on the React Native versions that offer it: the native code is #ifdef-guarded and codegen runs for this library either way, so newArchEnabled=false builds and works on Android. Note that on iOS from React Native 0.85, React Native itself forces RCT_NEW_ARCH_ENABLED=1 during pod install, so the New Architecture is always used there regardless of what you configure.

Usage

import AppShortcuts from '@mbdayo/react-native-app-shortcuts';

// Check if shortcuts are supported
const checkSupport = async () => {
  const isSupported = await AppShortcuts.isSupported();
  console.log('Shortcuts supported:', isSupported);
};

// Set dynamic shortcuts
const setShortcuts = async () => {
  const shortcuts = [
    {
      type: 'compose', // unique identifier
      title: 'New Message',
      subtitle: 'Create a new message', // optional
      symbolName: 'square.and.pencil', // optional, SF Symbol, iOS only
      iconName: 'compose_icon', // optional, asset catalog / res/drawable
      userInfo: { screen: 'Compose' }, // optional, comes back on the event
    },
    {
      type: 'search',
      title: 'Search',
      iconName: 'search_icon',
    },
  ];

  const success = await AppShortcuts.setShortcuts(shortcuts);
  console.log('Shortcuts set:', success);
};

// Clear all shortcuts
const clearShortcuts = async () => {
  const success = await AppShortcuts.clearShortcuts();
  console.log('Shortcuts cleared:', success);
};

// Check if app was launched from a shortcut (consume-once)
const checkInitialShortcut = async () => {
  const shortcut = await AppShortcuts.getInitialShortcut();
  if (shortcut) {
    handleShortcut(shortcut);
  }
};

// Listen for shortcut selection while the app is running
useEffect(() => {
  checkInitialShortcut();

  const removeListener = AppShortcuts.addListener(handleShortcut);
  return removeListener;
}, []);

// Handle shortcut action
const handleShortcut = (event) => {
  switch (event.type) {
    case 'compose':
      // Navigate to compose screen, using event.userInfo if you attached any
      break;
    case 'search':
      // Navigate to search screen
      break;
  }
};

A runnable example lives in example/.

Platform support

Feature iOS Android
setShortcuts / clearShortcuts ✅ ✅ (API 25+)
getInitialShortcut (cold launch) ✅ ✅
addListener (warm tap) ✅ ✅
subtitle ✅ ✅ (long label)
iconName ✅ asset catalog ✅ res/drawable
symbolName (SF Symbols) ✅ iOS 13+ ➖ ignored
userInfo ✅ ✅

getInitialShortcut() is consume-once on both platforms: the second call resolves null unless another shortcut has been used since.

Icons

iOS

Use symbolName for an SF Symbol (e.g. 'square.and.pencil'), or iconName for an image in your app's asset catalog. If both are given, symbolName wins.

Android

iconName must match a drawable resource name in res/drawable (without the file extension). If the drawable is not found the shortcut is still created, without an icon. symbolName is ignored.

API Reference

Methods

isSupported(): Promise<boolean>

Whether dynamic shortcuts are supported. Always true on iOS (the minimum deployment target is already above the iOS 9 floor for Quick Actions); on Android, true from API 25 up.

getInitialShortcut(): Promise<ShortcutEvent | null>

The shortcut the app was launched from, or null. Consume-once.

setShortcuts(shortcuts: Shortcut[]): Promise<boolean>

Replace the app's dynamic shortcuts. Rejects if any shortcut is missing type or title.

clearShortcuts(): Promise<boolean>

Remove all dynamic shortcuts.

addListener(callback: (event: ShortcutEvent) => void): () => void

Listen for shortcuts used while the app is running. Returns a function that removes the listener.

Types

Shortcut

interface Shortcut {
  type: string; // Unique identifier
  title: string; // Display title
  subtitle?: string; // Optional subtitle / long label
  iconName?: string; // Optional asset catalog or drawable name
  symbolName?: string; // Optional SF Symbol name (iOS only)
  userInfo?: Record<string, unknown>; // Optional extra data
}

ShortcutEvent

interface ShortcutEvent {
  type: string; // The type of the shortcut that was used
  userInfo?: Record<string, unknown>; // Whatever you attached to the shortcut
  /** @deprecated Read `type` instead. Removed in 3.0.0. */
  shortcutId: string;
}

Migrating from 1.x to 2.0.0

1.x could not install or build on React Native 0.76+, and the API the README documented was rejected by the native code. 2.0.0 makes one contract true everywhere: type identifies a shortcut and userInfo carries its extra data, on the JS types, the codegen spec, and both native implementations.

Breaking changes

1.x 2.0.0 Notes
setShortcuts([{ id, data }]) setShortcuts([{ type, userInfo }]) id/data still accepted at runtime until 3.0.0
event.shortcutId event.type shortcutId still emitted until 3.0.0
getInitialShortcut() repeatable on Android consume-once on both iOS already behaved this way
Pod name @mbdayo/react-native-app-shortcuts Pod name RNAppShortcuts changes the header import, below
React Native * React Native >=0.76
Android minSdkVersion 21 minSdk 24 matches React Native 0.76+

What you need to change

1. Rename id → type and data → userInfo when calling setShortcuts:

 AppShortcuts.setShortcuts([
-  { id: 'compose', title: 'New Message', data: { screen: 'Compose' } },
+  { type: 'compose', title: 'New Message', userInfo: { screen: 'Compose' } },
 ]);

The old keys still work for one release, so you can do this after upgrading. They are removed in 3.0.0.

2. Read event.type instead of event.shortcutId:

-AppShortcuts.addListener((event) => handle(event.shortcutId));
+AppShortcuts.addListener((event) => handle(event.type));

shortcutId is still present on the event until 3.0.0.

3. Update the iOS header import. The pod is now named RNAppShortcuts, because @ and / are not valid in a CocoaPods pod name:

-#import <@mbdayo/react-native-app-shortcuts/RNAppShortcuts.h>
+#import <RNAppShortcuts/RNAppShortcuts.h>

4. Re-run pod install and rebuild both platforms.

5. If you called getInitialShortcut() more than once on Android and relied on it returning the same value every time, keep the result yourself — it is now consume-once, matching iOS.

Fixed in 2.0.0

  • pod install failed on React Native 0.76+ (stale React-Codegen/RCT-Folly dependencies, and an invalid pod name). The podspec now delegates to install_modules_dependencies.
  • RNAppShortcuts.h was invalid Objective-C under the New Architecture (two adjacent protocol lists).
  • The Android module did not configure under AGP 8 (no namespace, no manifest, stale compileSdk/AGP/react-native:+ dependency, no codegen wiring).
  • A hand-written BuildConfig.java read a JVM system property that is never set on Android, so the module was never registered as a TurboModule.
  • setShortcuts rejected every call written the way the README documented.
  • Android shortcuts could not launch the app — the intent targeted ReactApplicationContext, which is not a component. It now targets the launcher activity.
  • The iOS warm-tap event never fired (it checked bridge on a freshly allocated, uninitialised object). It now emits from the live instance, gated on attached listeners.
  • Android re-fired the shortcut event on every resume, because the launching intent's extra was never cleared.
  • Importing the package threw when the native module was missing, instead of failing at first use.

License

MIT

About

React Native package for iOS UIApplicationShortcutItem and Android App Shortcuts

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages