Docs
grovs.io Start for free

Quick start

Install and configure the Grovs React Native SDK in your app.

Install the wrapper, wire the native side and open your first link on a device. Takes about fifteen minutes, less with Expo.

Using an AI coding agent?

Copy one prompt into Claude Code, Cursor or Copilot and it does these steps in your project.

Open in Cursor

This page covers wrapper 3.1.0, which wraps iOS SDK 3.1 and Android SDK 3.1.0. Upgrading from 3.0.x? See the upgrade guide. From 1.x, see the 3.0 upgrade guide.

Requirements

  • React Native 0.70+, new architecture (TurboModules) or legacy bridge
  • iOS 13.0+ and Android 5.0 (API 21)+, the native SDK minimums. Your React Native version may need more.
  • A Swift AppDelegate and Kotlin MainApplication / MainActivity, as current React Native and Expo templates generate. The snippets and the Expo plugin assume them.

Installation

Bash
# Using npm
npm install react-native-grovs-wrapper
 
# Using yarn
yarn add react-native-grovs-wrapper

Android dependency

MainApplication and MainActivity call Grovs directly, so the app needs its own Android SDK dependency, at the version the wrapper is built against. Add it to android/app/build.gradle:

Groovy
dependencies {
    implementation 'io.grovs:Grovs:3.1.0'
}

Wrapper 3.0.2 uses 3.0.1, and 3.0.0 and 3.0.1 use 3.0.0.

It's on Maven Central, so no extra repository is needed. The Expo config plugin adds this line for you.

iOS dependency

The wrapper's podspec depends on Grovs ~> 3.1 (~> 3.0 in wrapper 3.0.x), so pod install in ios/ adds the iOS SDK. Run pod install again after upgrading the wrapper.

Expo integration

With an Expo development build (expo-dev-client, not Expo Go), the config plugin automates the native setup below. Add it to app.json:

JSON
{
  "plugins": [
    ["react-native-grovs-wrapper", {
      "apiKey": "your-api-key",
      "scheme": "your_app_scheme",
      "useTestEnvironment": false,
      "associatedDomains": ["your_app_host", "your_app_test_host"],
      "baseURL": "https://sdk.example.com",
      "clipboardDomains": ["links.yourdomain.com"]
    }]
  ]
}
PropertyRequiredDefaultDescription
apiKeyYesYour Grovs API key
schemeYesCustom URL scheme for deep links, from the Grovs dashboard
useTestEnvironmentNofalseUse your project's test environment
associatedDomainsNo[]Universal link and App Link hosts. Sent to iOS as applinks: entries, adding the prefix if missing
baseURLNononeBase URL of a self-hosted backend
clipboardDomainsNo[]Extra link hosts accepted by clipboard-assisted deferred deep linking. Must be an array of strings; Grovs hosts are always accepted
pushNofalseAdds the native push setup
iosRichPushNofalseCreates the iOS Notification Service Extension for campaign images. Needs push and ios.bundleIdentifier
appleTeamIdNononeApple team that signs the extension. ios.appleTeamId isn't read
androidNotificationIconNononeDrawable name for the Android small notification icon

The push options need wrapper 3.1.0.

The plugin adds:

  • the URL scheme and Associated Domains to the iOS project
  • the intent filters to AndroidManifest.xml
  • the io.grovs:Grovs dependency to android/app/build.gradle
  • the configure and link-forwarding code to AppDelegate.swift, MainApplication.kt and MainActivity.kt

The injected configure calls pass autoTrackScreenViews: false, so the native SDKs don't report the React Native host as a screen. They pass enabled: GrovsWrapperSwift.isSDKEnabled() / enabled = GrovsConsent.isEnabled(this), so a setSDK consent choice survives a cold start. Objective-C or Java app files fail with an error.

Run npx expo prebuild, then build with npx expo run:ios / npx expo run:android. The plugin is tested with Expo SDK 58.

The plugin skips any file that already contains Grovs.configure. After upgrading the wrapper or changing plugin options, run npx expo prebuild --clean to regenerate the configure calls. Save manual native edits first.

Android configuration

1. Initialize the SDK

In your MainApplication class:

Kotlin
import io.grovs.Grovs
import com.grovswrapper.GrovsConsent
 
override fun onCreate() {
    super.onCreate()
    Grovs.configure(
        this,
        "your-api-key",
        useTestEnvironment = false,
        baseURL = null,                          // or "https://sdk.example.com" for a self-hosted backend
        autoTrackScreenViews = false,            // the native tracker only sees the React Native host Activity
        clipboardDomains = null,                 // or listOf("links.yourdomain.com")
        enabled = GrovsConsent.isEnabled(this)   // the value JavaScript last passed to setSDK, true by default
    )
}

Android configure has no defaults after baseURL, so pass every parameter. Keep autoTrackScreenViews = false: native tracking can't see individual React Native screens. Track them from JavaScript with startScreenTracking. GrovsConsent.isEnabled reads the value stored by setSDK (see Privacy & Consent). Parameters: Android Quick Start.

In your MainActivity class:

Kotlin
import android.content.Intent
import io.grovs.Grovs
 
override fun onStart() {
    super.onStart()
    Grovs.onStart(launcherActivity = this)
}
 
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    Grovs.onNewIntent(intent, launcherActivity = this)
}

3. Add intent filters

Add these to your launcher activity in AndroidManifest.xml:

XML
<!-- Custom URL scheme -->
<intent-filter>
    <data android:scheme="your_app_scheme" android:host="open" />
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
 
<!-- App links (production) -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="your_app_host" />
</intent-filter>
 
<!-- App links (test) -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="your_app_test_host" />
</intent-filter>

iOS configuration

1. Initialize the SDK

In your AppDelegate.swift:

Swift
import Grovs
import react_native_grovs_wrapper
 
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Grovs.configure(
        APIKey: "your-api-key",
        useTestEnvironment: false,
        // baseURL: "https://sdk.example.com",        // self-hosted backend
        autoTrackScreenViews: false,                   // the native tracker only sees the React Native host view controller
        clipboardDomains: nil,                         // or ["links.yourdomain.com"]
        enabled: GrovsWrapperSwift.isSDKEnabled(),     // the value JavaScript last passed to setSDK, true by default
        delegate: GrovsWrapperSwift.shared             // the wrapper delivers links to JavaScript
    )
    // Optional: verbose logging while integrating
    Grovs.setDebug(level: .info)
 
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

Keep the super call and return its result; RCTAppDelegate bootstraps React Native there.

Pass GrovsWrapperSwift.shared as the delegate, as the Expo plugin does. nil also works: the wrapper sets itself as the delegate once launch finishes. Don't pass your own GrovsDelegate; the wrapper replaces it. GrovsWrapperSwift.isSDKEnabled() reads the value stored by setSDK (see Privacy & Consent). Parameters: iOS Quick Start.

Add to your AppDelegate.swift:

Swift
override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    return Grovs.handleAppDelegate(continue: userActivity, restorationHandler: restorationHandler)
}
 
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    return Grovs.handleAppDelegate(open: url, options: options)
}

If your AppDelegate already implements one (to chain RCTLinkingManager, say), call Grovs.handleAppDelegate first inside it and return true when it handled the link.

3. Configure Associated Domains

  1. Open the iOS project in Xcode.
  2. Select your app target → Signing & Capabilities.
  3. Add the Associated Domains capability.
  4. Add applinks:your_app_host and applinks:your_app_test_host.

4. Configure URL scheme

  1. In Xcode, select your target → Info tab.
  2. Under URL Types, click +.
  3. Add the production and test URL schemes from your Grovs dashboard.

Basic usage

typescript
import Grovs from 'react-native-grovs-wrapper';
 
// Optional: identify the user
Grovs.setIdentifier('user_id_from_your_app');
Grovs.setAttributes({ name: 'John Doe', plan: 'premium' });
 
// Receive deep links
const listener = Grovs.onDeeplinkReceived((response) => {
    console.log('Link:', response.link, 'Data:', response.data);
});

Next steps

See also

Last updated 2026-10-27
Was this page helpful?