# In-app messages

> Show in-app messages from the Grovs dashboard in your React Native app, hold them back on some screens, and react to taps and answers.

Source: https://docs.grovs.io/docs/sdk/react-native/in-app-messages

In-app messages are dashboard campaigns: a banner, modal, full-screen cover, system alert, question or your own HTML. The native SDKs download live campaigns, pick one on the device for each app open, screen view or event, and draw it above your React Native views. To create campaigns, see [In-app messages](https://docs.grovs.io/docs/engagement/in-app-messages).

No code is required. Once the SDK is [configured](https://docs.grovs.io/docs/sdk/react-native/quick-start), campaigns show. Use the calls below to keep messages off some screens, pause them during a flow, or react to what the user did.

In-app messages need the next `react-native-grovs-wrapper` release (iOS SDK 3.1, Android SDK 3.1.0).

## Triggers

A campaign triggers on app open, a screen view or a custom event from [`track`](https://docs.grovs.io/docs/sdk/react-native/analytics#tracking-custom-events). React Native apps turn native screen tracking off (`autoTrackScreenViews: false`), so screen triggers and excluded screens use the names you send with [`startScreenTracking`](https://docs.grovs.io/docs/sdk/react-native/analytics#automatic-screen-tracking-with-react-navigation) or `trackScreenView`.

Sync, frequency caps, priority and offline behaviour are native: see [How messages reach the device](https://docs.grovs.io/docs/sdk/ios/in-app-messages#how-messages-reach-the-device).

## React to messages

`onInAppMessage` gets an event for each step of a message:

```typescript
import Grovs from 'react-native-grovs-wrapper';

const subscription = Grovs.onInAppMessage((event) => {
    switch (event.type) {
        case 'clicked':
            // payload is set for a key-value action
            if (event.payload?.screen === 'refill') {
                navigation.navigate('Refill');
            }
            break;
        case 'answered':
            console.log(event.campaignId, event.value, event.comment);
            break;
    }
});

// When you no longer need it
subscription.remove();
```

| `type`      | Fields                                | When                                                                                                                          |
| ----------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `shown`     | `campaignId`                          | The message is on screen.                                                                                                     |
| `clicked`   | `campaignId`, `buttonId?`, `payload?` | A button or the message was tapped. `payload` carries the pairs of a key-value action.                                        |
| `dismissed` | `campaignId`, `reason`                | The message closed. `reason` is `close_button`, `backdrop`, `swipe`, `app` or `orientation`. Other values may be added later. |
| `answered`  | `campaignId`, `value`, `comment?`     | A question was answered. `value` is a number for stars, or the chosen option's ID (string).                                   |
| `failed`    | `message`, `campaignId?`              | The message couldn't be shown. `campaignId` is set on Android only.                                                           |

Events that happen before your first `onInAppMessage` call, such as a message shown on app open, are kept (up to 20, oldest dropped first) and delivered when you subscribe. After a JavaScript reload they're kept again until the next subscription.

> **Warning:** The wrapper owns the native in-app delegate (`Grovs.inAppDelegate` on iOS) and listener (`Grovs.setInAppListener` on Android). Don't set them in native code, or `onInAppMessage` stops receiving events.

There's no way to veto a single message from JavaScript, unlike the native `grovsShouldShowInAppMessage`. Use pause, discard and excluded screens.

## Button actions

Each button, and the message itself, has one action. The native SDK performs it, reports the click and sends a `clicked` event.

| Action                  | What happens                                                                                                                                                                                                          |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Close                   | Closes the message.                                                                                                                                                                                                   |
| Open URL                | Opens the URL.                                                                                                                                                                                                        |
| Key-value               | Closes the message and sends the pairs as `payload`. Route in your own code.                                                                                                                                          |
| Request push permission | Shows the system prompt, or opens your app's notification settings if the person declined before. On Android 13+ it needs `POST_NOTIFICATIONS` in your manifest; without it, campaigns with this action aren't shown. |
| Request app rating      | iOS shows the system review prompt. Android needs the Play review library in your app; without it, campaigns with this action aren't shown.                                                                           |
| Open store              | Opens your app's store page if the project has one configured. Otherwise campaigns with this action aren't shown.                                                                                                     |

For ratings on Android, add to `android/app/build.gradle`:

```groovy
implementation 'com.google.android.play:review-ktx:2.0.2'
```

## Hold messages back

```typescript
// Pause during checkout or a video. The newest highest priority message waits.
Grovs.pauseInAppMessages();

// Show messages again, starting with the one that waited, if it still qualifies.
Grovs.resumeInAppMessages();

// Drop the waiting message. dismissCurrent also closes the one on screen. Defaults to false.
Grovs.discardInAppMessages({ dismissCurrent: true });

// No message shows on these screens.
Grovs.setInAppExcludedScreens(['Checkout', 'Onboarding']);
```

Excluded screens match the names sent through `trackScreenView` or `startScreenTracking`. Each call replaces the list. Non-string entries are dropped.

## Questions and HTML messages

Star ratings, single-choice questions, comment boxes and custom HTML (`window.grovs`, data attributes, `{{user.*}}` placeholders) are drawn by the native SDKs. See [Questions](https://docs.grovs.io/docs/sdk/ios/in-app-messages#questions) and [Custom HTML messages](https://docs.grovs.io/docs/sdk/ios/in-app-messages#custom-html-messages). An answer arrives as an `answered` event.

## Test messages

**Send test** on a dashboard campaign sends it to every device of one person, matched by the identifier from [`setIdentifier`](https://docs.grovs.io/docs/sdk/react-native/api-reference#setidentifier). Without `setIdentifier`, test messages don't reach the device. The device shows the test at its next sync, ignoring triggers, conditions and limits. To see it sooner, background the app and return after a minute.

## Debugging

```typescript
Grovs.setDebug('info');
Grovs.setInAppDebugLogging(true);
```

`setInAppDebugLogging(true)` logs each campaign considered for an event and why it was skipped. Logs go to Xcode and Logcat, not the Metro console.

## Reserved event names

The SDK reports message views, clicks, dismissals and answers as events. [`track`](https://docs.grovs.io/docs/sdk/react-native/analytics#event-name-rules) refuses these names:

`in_app_viewed`, `in_app_clicked`, `in_app_dismissed`, `in_app_answered`, `in_app_commented`, `in_app_held`, `push_open`, `push_delivered`, `push_held`, `journey_entered`, `journey_held`, `journey_path`.

## The old inbox

`displayMessages()` and `numberOfUnreadMessages()` are deprecated. They resolve without showing anything, and the count is always `0`. Remove inbox buttons and unread badges. See [Deprecated](https://docs.grovs.io/docs/sdk/react-native/api-reference#deprecated).

## See also

- [Push notifications](https://docs.grovs.io/docs/sdk/react-native/push-notifications)
- [Events & screen tracking](https://docs.grovs.io/docs/sdk/react-native/analytics): the events and screens that trigger messages
- [API reference](https://docs.grovs.io/docs/sdk/react-native/api-reference#in-app-messages)
- [iOS in-app messages](https://docs.grovs.io/docs/sdk/ios/in-app-messages): native behaviour in detail
