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.
Copy one prompt into Claude Code, Cursor or Copilot and it does these steps in your project.
Add Grovs deep linking to this React Native app (example app: Medly). 1. Install react-native-grovs-wrapper. 2. Expo with a development build: add the react-native-grovs-wrapper config plugin to app.json (apiKey, scheme, useTestEnvironment, associatedDomains) and run npx expo prebuild --clean, then skip to step 5. 3. Android: add implementation 'io.grovs:Grovs:3.1.0' to android/app/build.gradle. Call Grovs.configure in MainApplication with autoTrackScreenViews = false and enabled = GrovsConsent.isEnabled(this). Forward onStart and onNewIntent in MainActivity. Add the intent filters. 4. iOS: call Grovs.configure in AppDelegate.swift with autoTrackScreenViews: false, enabled: GrovsWrapperSwift.isSDKEnabled() and delegate: GrovsWrapperSwift.shared. Forward incoming links, add the URL scheme and Associated Domains, then run pod install. 5. Read the API key from the existing secrets setup (env GROVS_API_KEY). Use the test environment only in debug builds. 6. Register Grovs.onDeeplinkReceived early in the app and route response.data to the existing navigation. 7. Build both platforms, then list what you changed. Docs: https://docs.grovs.io/docs/sdk/react-native/quick-start.md
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
AppDelegateand KotlinMainApplication/MainActivity, as current React Native and Expo templates generate. The snippets and the Expo plugin assume them.
Installation
# Using npm
npm install react-native-grovs-wrapper
# Using yarn
yarn add react-native-grovs-wrapperAndroid 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:
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:
{
"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"]
}]
]
}| Property | Required | Default | Description |
|---|---|---|---|
apiKey | Yes | Your Grovs API key | |
scheme | Yes | Custom URL scheme for deep links, from the Grovs dashboard | |
useTestEnvironment | No | false | Use your project's test environment |
associatedDomains | No | [] | Universal link and App Link hosts. Sent to iOS as applinks: entries, adding the prefix if missing |
baseURL | No | none | Base URL of a self-hosted backend |
clipboardDomains | No | [] | Extra link hosts accepted by clipboard-assisted deferred deep linking. Must be an array of strings; Grovs hosts are always accepted |
push | No | false | Adds the native push setup |
iosRichPush | No | false | Creates the iOS Notification Service Extension for campaign images. Needs push and ios.bundleIdentifier |
appleTeamId | No | none | Apple team that signs the extension. ios.appleTeamId isn't read |
androidNotificationIcon | No | none | Drawable 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:Grovsdependency toandroid/app/build.gradle - the
configureand link-forwarding code toAppDelegate.swift,MainApplication.ktandMainActivity.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:
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.
2. Handle incoming links
In your MainActivity class:
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:
<!-- 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:
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.
2. Handle incoming links
Add to your AppDelegate.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
- Open the iOS project in Xcode.
- Select your app target → Signing & Capabilities.
- Add the Associated Domains capability.
- Add
applinks:your_app_hostandapplinks:your_app_test_host.
4. Configure URL scheme
- In Xcode, select your target → Info tab.
- Under URL Types, click +.
- Add the production and test URL schemes from your Grovs dashboard.
Basic usage
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
- Handle incoming deep links →
- Generate smart links →
- Receive push campaigns →
- React to in-app messages →
- Track events and screens →
- Gate collection on user consent →
- Explore the React Native example app →
See also
- Integrating an npm package: install the wrapper with npm
- Integrating a Yarn Package: install with yarn
- How to Add an Associated Domain: Xcode steps in detail
- How to Add a New Intent Filter: Android intent filters in detail
- Deep link not opening the app: links open the browser instead of the app
- SDK configuration issues: common setup and initialization problems