https://github.com/avas-app/react-native-app-intents
https://github.com/avas-app/react-native-app-intents
android intents ios react-native turbo-modules
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/avas-app/react-native-app-intents
- Owner: avas-app
- Created: 2026-05-10T03:47:54.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-21T04:19:51.000Z (about 2 months ago)
- Last Synced: 2026-05-21T10:57:00.646Z (about 2 months ago)
- Topics: android, intents, ios, react-native, turbo-modules
- Language: TypeScript
- Homepage: https://avas-app.github.io/react-native-app-intents/
- Size: 858 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# react-native-app-intents
Type-safe App Intents, App Shortcuts, and dynamic shortcut helpers for React Native.
```bash
npm install @avasapp/react-native-app-intents
```
Full usage docs are set up for GitHub Pages at:
`https://avasapp.github.io/react-native-app-intents/`
## Basic setup
Create `app-intents.config.ts`:
```ts
import { defineAppIntentsConfig } from "@avasapp/react-native-app-intents/codegen";
export default defineAppIntentsConfig({
intents: ["src/**/*.intents.ts"],
scheme: "myapp",
ios: {
output: "ios/MyApp/AppShortcuts.swift",
bundleIdentifier: "com.example.myapp",
siriUsageDescription: "Used to let Siri run app actions.",
},
android: {
manifest: "android/app/src/main/AndroidManifest.xml",
shortcutsOutput: "android/app/src/main/res/xml/app_shortcuts.xml",
packageName: "com.example.myapp",
},
types: { output: "src/generated/app-intents.d.ts" },
});
```
Define intents:
```ts
import { defineIntent, p } from "@avasapp/react-native-app-intents";
export const openOrder = defineIntent({
id: "openOrder",
title: "Open Order",
phrases: ["Open order ${orderNumber} in ${.applicationName}"],
params: {
orderNumber: p.string({
androidBiiParam: "order",
title: "Order number",
default: "1234",
}),
},
surfaces: {
spotlight: true,
appShortcut: {
icon: {
androidResourceName: "@mipmap/ic_launcher_round",
systemName: "shippingbox",
},
},
},
android: {
appAction: {
capability: "actions.intent.GET_ORDER",
},
},
ios: {
appIntent: {},
},
});
```
Generate native files:
```bash
npx app-intents generate
```
Handle intents in JS:
```ts
import { createAppIntentsRuntime } from "@avasapp/react-native-app-intents";
import { openOrder } from "./orders.intents";
const appIntents = createAppIntentsRuntime({
scheme: "myapp",
intents: [openOrder] as const,
});
appIntents.onIntent(openOrder, (params) => {
// Navigate to the requested order.
});
```
App-intents URLs use the reserved `app-intents` host, such as
`myapp://app-intents/openOrder?...`. You can share `myapp` with normal app deep
links, but route only the `app-intents` host through this library and let React
Native `Linking` handle the rest.
For auth-gated apps, donate only after a real user action, and clear donations
plus dynamic shortcuts on logout or when the feature is disabled:
```ts
import { useEffect } from "react";
async function handleOpenedOrder(orderNumber: string) {
await appIntents.donate(openOrder, { orderNumber });
}
useEffect(() => {
if (!session || !flags.orderShortcuts) {
void appIntents.clearDonations();
void appIntents.updateDynamicShortcuts([]);
return;
}
void appIntents.updateDynamicShortcuts([
{
icon: {
androidResourceName: "@mipmap/ic_launcher_round",
systemName: "shippingbox",
},
intent: openOrder,
params: { orderNumber: session.lastViewedOrderNumber },
shortTitle: "Open last order",
longTitle: `Open order ${session.lastViewedOrderNumber}`,
},
]);
}, [session, flags.orderShortcuts]);
async function logout() {
await auth.signOut();
await appIntents.clearDonations();
await appIntents.updateDynamicShortcuts([]);
}
```
`surfaces.appShortcut` still accepts `true`, but you can also pass an object to set
an iOS SF Symbol (`systemName`) and/or an Android shortcut resource reference
(`androidResourceName`, such as `@mipmap/ic_launcher_round`).
## Android App Actions contract
- Use `android.appAction` to opt an intent into Android App Actions.
- Android App Actions are the primary Android target; Google Assistant voice support is best-effort.
- `surfaces.assistant` and top-level `androidBii` are legacy shims and should be avoided in new intent definitions.
## Android App Actions support matrix
| Scenario | Status | Coverage |
| ----------------------------------------------------------- | --------------------- | ----------------------------------------------- |
| `actions.intent.GET_ORDER` scalar slot binding | Supported | Core validation, codegen snapshot, runtime test |
| `actions.intent.GET_ORDER` entity-backed shortcut inventory | Supported | Core validation, codegen snapshot, example app |
| Capability-bound Android donations and dynamic shortcuts | Supported | Runtime test, Android native module wiring |
| Legacy `surfaces.assistant` / top-level `androidBii` usage | Compatibility only | Core validation |
| Google Assistant voice triggering | Best effort | Manual verification only |
| Verified App Links / Play Console review steps | Manual setup required | Codegen diagnostics |
For an opt-in `adb` smoke test against the bare Android example app, run:
```bash
RN_APP_INTENTS_ANDROID_E2E=1 bun test packages/react-native/test/android-app-actions.e2e.test.ts
```
## iOS Siri / App Intents contract
- Use `ios.appIntent` to opt an intent into native Siri/App Intents generation.
- `surfaces.siri` no longer enables App Intents by itself; keep using `surfaces.appShortcut` and `surfaces.spotlight` for those separate surfaces.
- Static `ios.appIntent.response.dialog` is supported only for background intents; it cannot be combined with `behavior.opensAppToForeground`.
- `object` params are flattened into Swift leaf parameters for App Intents, but phrases cannot interpolate the object parameter itself.
## iOS Siri / App Intents support matrix
| Scenario | Status | Coverage |
| -------------------------------------------------------- | ----------------- | -------------------------------------------- |
| `ios.appIntent` foreground URL handoff | Supported | Codegen snapshot, runtime tests, example app |
| Static `ios.appIntent.response.dialog` | Supported | Core validation, Swift typecheck, snapshot |
| Nested `object` params in generated App Intents | Supported | Core validation, Swift typecheck, snapshot |
| `surfaces.siri` without `ios.appIntent` | Unsupported | Core validation |
| Object-param placeholders inside `phrases` | Unsupported | Core validation |
| Custom bundled image assets in generated App Shortcuts | Unsupported | Documentation only |
| Dynamic Siri dialog sourced from JS/native perform logic | Not yet supported | Explicit non-goal for current slice |
## Expo setup
Use the package root as an Expo config plugin. The plugin reads the same
`app-intents.config.ts` file used by the CLI, so `app.config.ts` does not need to
duplicate your intent configuration:
```json
{
"expo": {
"plugins": ["@avasapp/react-native-app-intents"]
}
}
```
To use a different config file, pass `configPath`:
```json
{
"expo": {
"plugins": [["@avasapp/react-native-app-intents", { "configPath": "./config/app-intents.ts" }]]
}
}
```
In Expo prebuilds, configured `ios.output`, `android.manifest`,
`android.shortcutsOutput`, and `android.shortcutsStringsOutput` paths are honored
relative to the app root. If `ios.output` does not start with `ios/`, it is
written under the generated iOS project folder. The generated Android manifest
also ensures `MainActivity` uses a foreground-intent-compatible launch mode for
Assistant/App Action deep links and adds the `scheme://app-intents` filter
without replacing existing app or Expo dev-client deep-link filters.
### Custom shortcut icons with `expo-asset`
For custom **Android** shortcut icons in Expo apps, add the image files to native
resources with the `expo-asset` config plugin, then reference the generated
resource name from `androidResourceName`:
```json
{
"expo": {
"plugins": [
["expo-asset", { "assets": ["./assets/shortcuts/open_order.png"] }],
"@avasapp/react-native-app-intents"
]
}
}
```
```ts
surfaces: {
appShortcut: {
icon: {
androidResourceName: "@drawable/open_order",
systemName: "shippingbox",
},
},
}
```
For iOS **dynamic shortcuts**, you can also point at a bundled template image
name from the same asset file:
```ts
await appIntents.updateDynamicShortcuts([
{
icon: {
androidResourceName: "@drawable/open_order",
iosTemplateImageName: "open_order",
systemName: "shippingbox",
},
intent: openOrder,
params: { orderNumber: "1234" },
shortTitle: "Open order #1234",
},
]);
```
`expo-asset` uses the file name as the native resource name, so keep shortcut
asset files lowercase with underscores (for example, `open_order.png`). Run
`npx expo prebuild` again after adding or renaming assets.
On iOS, `iosTemplateImageName` only applies to **dynamic shortcuts** and renders
as a template/tinted silhouette. Generated App Shortcuts still use `systemName`
only; Expo-bundled PNG assets are not used there.
## Current features
- Single scoped npm package: runtime, authoring API, codegen, CLI, and Expo plugin.
- Type-safe `defineIntent`, `defineEntity`, and `p.*` parameter builders.
- First-class `android.appAction` authoring plus Android shortcuts XML, strings XML, and manifest patching.
- First-class `ios.appIntent` authoring plus Swift App Intents/App Shortcuts generation.
- Nested object-parameter support in generated iOS App Intents, including generated parameter summaries.
- Static iOS App Intent dialog responses via `ios.appIntent.response.dialog`.
- Generated TypeScript event types.
- Runtime helpers for initial intents, warm intent events, intent URL parsing/building, donations,
donation clearing, and dynamic shortcuts.
- Expo prebuild plugin for iOS codegen, URL scheme setup, app group entitlements, quick-action forwarding, and Android foreground deep-link manifest setup.
- Bare React Native iOS and Android native modules.
## Planned features
- Expanded Android App Actions coverage.
- Dynamic Siri/App Intent dialog flows beyond the current static-response slice.
- Richer icons and metadata for generated shortcuts.
- More Expo and bare-app setup automation.
- Expanded examples and end-to-end app templates.
- More generated type helpers for navigation integrations.
## Development
```bash
bun install
bun run typecheck
bun run lint
bun run test
bun run build
```
The repository is a Bun workspace. `packages/react-native` is the public
`@avasapp/react-native-app-intents` package; the other workspace packages are private internal boundaries.