Installation guides
React Native and Expo
Track screens, goals and signed-in users in React Native and Expo apps, attributed to the deep link or Play Store campaign behind each install.
@productlense/react-native sends screen views, goals and identify from your app to ProductLense. It has no native modules, so it works in Expo Go, a development build and bare React Native, and it is about 3 KB gzipped. Every report works for app traffic the way it does for the web: the Hostname breakdown shows your bundle id next to your website, so you can see web and app side by side.
1. Add your app ID
In ProductLense, open Settings → General → App IDs and add your iOS bundle identifier and Android application id, for example com.example.app. Events from any other app id are rejected, the same way hostnames protect your website.
2. Install
npx expo install @productlense/react-native @react-native-async-storage/async-storagenpm install @productlense/react-native @react-native-async-storage/async-storageAsyncStorage keeps the install id, the opt-out choice and unsent events across launches. Without it, ProductLense still works but counts each launch as a new install.
3. Start ProductLense once
import AsyncStorage from '@react-native-async-storage/async-storage';
import ProductLense from '@productlense/react-native';
import * as Application from 'expo-application';
ProductLense.init({
siteId: 'YOUR_SITE_ID',
appId: 'com.example.app',
storage: AsyncStorage,
installReferrer: () => Application.getInstallReferrerAsync(), // Android only; optional
});siteId is on Settings → Snippet. Call init at module level or in your root component, before the first screen renders. Every other call made before init is ignored, and no call ever throws.
4. Track screens
React Navigation
import { NavigationContainer, createNavigationContainerRef } from '@react-navigation/native';
import ProductLense from '@productlense/react-native';
import { useProductLenseNavigation } from '@productlense/react-native/navigation';
const navigationRef = createNavigationContainerRef();
export default function App() {
useProductLenseNavigation(navigationRef, ProductLense);
return <NavigationContainer ref={navigationRef}>{/* screens */}</NavigationContainer>;
}Each route change is one screen view named after the route, so a Settings route shows as the page /Settings.
Expo Router
import { Stack } from 'expo-router';
import ProductLense from '@productlense/react-native';
import { useProductLenseScreens } from '@productlense/react-native/expo-router';
export default function RootLayout() {
useProductLenseScreens(ProductLense);
return <Stack />;
}Screens are named after the route path, such as /settings/billing.
By hand
ProductLense.screen('Checkout');5. Goals and users
ProductLense.track('signup', { plan: 'pro' }); // a goal, with up to 2 KB of flat props
ProductLense.identify(user.id, { name: user.name, email: user.email });Call identify after sign-in with the same user id your billing provider stores on the customer. App visits, web visits and payments then join up into one customer, so a subscription bought on your website is credited to the campaign that brought the app install. See how payments are attributed.
In-app purchases
Connect RevenueCat, then pass ProductLense's install id to it once, after both SDKs are configured:
import Purchases from 'react-native-purchases';
Purchases.setAttributes(await ProductLense.revenueCatAttributes());This sets the pl_install_id subscriber attribute, so each App Store or Google Play purchase lands on the channel that brought the install, even before the user signs in. Payments your backend takes through Stripe, Razorpay or any other billing connection are attributed through identify.
Attribution
- Deep links: links that open your app (
example://open?utm_source=newsletter, universal links and app links) are read automatically. Onlyutm_*,ref,via,sourceand click ids leave the device; tokens and other parameters do not. - Play Store: on Android, pass
installReferreras above. Theutm_*tags in your Play Store link are applied to the first screen of the install. - App Store: Apple does not share a referrer. Installs from the App Store count as Direct unless they open through a tagged universal link.
Attribution then follows the same first-touch rules as the web; see How ProductLense counts.
Privacy
- On first launch the SDK creates a random install id. It is not the advertising id (IDFA or AAID), and ProductLense never reads device identifiers, so no App Tracking Transparency prompt is needed for ProductLense.
- ProductLense stores a hash of the install id, never the id itself. It stays the same until the app is deleted or the user opts out, which is what lets reports count returning users and retention.
ProductLense.optOut()stops collection, deletes unsent events and forgets the install id.ProductLense.optIn()resumes with a new id. Offer it in your privacy settings if your users expect it.- Apple's App Store privacy label: declare Product Interaction (screen views and goals) as Analytics, not linked to the user's identity unless you call
identify, and not used for tracking.
Delivery
Events are sent in batches every 10 seconds, when 20 are waiting and when the app goes to the background. Offline, they wait on the device (up to 500) and are retried with backoff. The SDK only uses fetch, so a proxy works the same as for the web: pass apiHost: 'https://stats.example.com'.
Verify your installation
- Run the app on a device or simulator and open a few screens.
- Open the Live page in the ProductLense dashboard. Your device appears within a few seconds.
- Open Events: each screen is a
pageviewrow with your bundle id as the hostname andiOS apporAndroid appas the browser. - Pass
debug: truetoinitto log dropped events and failed sends.
If nothing appears, check that the app id is listed in Settings and follow Verify your installation.