Source of truth for package overview.
Add share extensions, action extensions, App Clips, iMessage apps, stickers, wallet extensions, and other Apple targets that Expo does not ship. React Native UIs are supported where the platform allows them.
Widgets: Native WidgetKit and Live Activities are first-class in this library. Official
expo-widgetsis an alternate React / Expo-UI path. Do not run both WidgetKit generators in one app. Android widgets are first-class Glance / Compose.LiveActivity.*on Android is an ongoing-notification helper. See widgets.md and limits.md.
Important: You must use a development build (
npx expo run:ios). Expo Go is not supported.Prerequisites: macOS, Xcode 14+, iOS 14+. Tested on Expo SDK 57. Full requirements →
npm install expo-targetsnpx expo-targets add
# interactive — or: npx expo-targets add share my-shareexpo-targets add scaffolds the target and wires the host by default (package.json, config plugin, App Groups, metro.config.js). If wiring added expo-targets to package.json, install dependencies.
If you use --no-wire or a dynamic app.config.js, add the plugin and App Groups yourself.
After npx expo prebuild, sealed build artifacts land in ios/<App>/ExpoTargetsGenerated/<Product>/ (gitignored). Deepen Swift under targets/*/ios/. Never edit ExpoTargetsGenerated/.
{
"expo": {
"plugins": ["expo-targets"],
"ios": {
"bundleIdentifier": "com.yourcompany.yourapp",
"entitlements": {
"com.apple.security.application-groups": [
"group.com.yourcompany.yourapp"
]
}
}
}
}Why App Groups? App Groups share data between your app and extensions. The ID must start with
group.. Convention:group.{your.bundle.identifier}.
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withTargets } = require("expo-targets/metro");
module.exports = withTargets(getDefaultConfig(__dirname));See React Native Extensions. (withTargetsMetro is a deprecated alias.)
npx expo-targets doctor
npx expo prebuild
npx expo run:iosimport { close, openHostApp, getSharedData } from "expo-targets";
const data = getSharedData(); // Content shared into the extension
openHostApp("/path"); // Open the host app
close(); // Dismiss the extensionShowcase subset (common adoption path). Full type set and maturity (~47 types): configuration.md.
| Type | iOS | Android | Description |
|---|---|---|---|
share |
✅ | ✅‡ | Share extensions (RN with entry on both; iOS .appex, Android dedicated Activity) |
action |
✅ | ✅‡ | Action extensions (RN with entry on both; iOS .appex, Android dedicated Activity) |
clip |
✅ | — | App Clips (RN UI supported) |
messages |
✅ | — | iMessage apps (RN UI supported) |
stickers |
✅ | — | iMessage sticker packs (asset-only) |
widget |
✅ | ✅ | Home screen widgets + Live Activities |
notification-service |
✅ | ✅§ | Push mutation (Android: local NotificationCompat; FCM leftover) |
notification-content |
✅ | ✅§ | Rich UI (Android: RemoteViews / A12 clamp) |
Legend: ✅ Production ready · 🔜 Planned · — Not applicable
‡Android share/action: dedicated Activities + RN
entryhost (TTI + Devicewright green — spikeandroid-rn-host-tti-2026-08-10.md). Showcase mark waits on notification FCM close. §Android notifications: host-process; FCM receive wired — shade green withFCM_*retires this mark. Widget ownership and Android LA → ongoing-notification: widgets.md, limits.md. Full matrix: configuration.md.Wallet, Safari, Network Extension family, file providers, and the rest: configuration.md. Lib floor vs Apple gates: limits.md. No new orphan stubs — deprecations.md.
expo-targets uses App Groups to share data between your app and extensions. For RN extensions, Metro plus a native host loads your React tree (iOS .appex process; Android dedicated Activity).
┌─────────────────┐ ┌─────────────────┐
│ Your App │ │ Extension │
│ │ │ │
│ setData / │───────▶│ UserDefaults / │
│ storage.set │ │ App Group │
│ getSharedData │◀───────│ RN host │
└─────────────────┘ └─────────────────┘
Thin hosts live under examples/. Start with share:
git clone https://github.com/csark0812/expo-targets.git
cd expo-targets/examples/share
npm install && npx expo run:ios| Example | What it shows |
|---|---|
| share | React Native share extension |
| action | React Native action extension |
| clip | React Native App Clip |
| messages | React Native messages extension |
| stickers | Asset-only sticker pack |
| widgets | iOS WidgetKit + Live Activities (widgets.md) |
| kitchen-sink | Five primary types in one host (messages, not stickers) |
| trick | Multi-target kitchen sink (Devicewright coverage host) |
See examples/README.md for the full suite (~48 hosts), Devicewright coverage, and stub READMEs.
- Getting Started — Build a React Native share extension
- Community and adoption — Public implementation reports, maintenance loops, and independently verifiable signals
- React Native Extensions — RN runtime contract + Metro; App Group OTA / eas update
- Widgets — WidgetKit / Live Activities ownership vs
expo-widgets - Configuration — All config options
- API Reference — JavaScript/TypeScript API (
ExtensionUpdates) - Deprecations — Roadmap freeze (no orphan stubs)
npx expo prebuild
npx expo run:iosnpx expo-targets sync
cd ios && pod installUse npx expo-targets sync --dry-run to preview. For new projects, use managed Expo and npx expo prebuild.
import { createTarget, close, openHostApp, getSharedData } from "expo-targets";
// RN extension entry
export const share = createTarget("MyShare", ShareExtension);
const data = getSharedData();
openHostApp("/inbox");
close();Shared storage (widgets and other targets):
const target = createTarget("MyTarget");
target.setData({ key: "value" });
// or: target.storage.set("key", "value");
target.refresh();See CONTRIBUTING.md (humans) and AGENTS.md (agent posture).
Built something with expo-targets? Use the project showcase form to share a public implementation without implying endorsement or production use.
MIT
Builds on community Apple-target / share-extension patterns and Expo’s official expo-widgets (React/Expo-UI alternate for widgets).