React Native
Overcentric has a dedicated SDK for React Native and Expo apps. It tracks screens, custom events, and JavaScript errors, and it works in Expo Go and bare React Native without any native module.
npm install overcentric-react-nativeimport { useEffect } from "react";
import overcentric from "overcentric-react-native";
export default function App() {
useEffect(() => {
overcentric.init({ projectId: "your-project-id" });
}, []);
return <YourApp />;
}That’s the minimum. Everything below is optional.
The Mobile Context
The web library asks you to choose between website and product. The React Native SDK does not — every event it sends carries context: "mobile" automatically.
| Context | Where it comes from |
|---|---|
website |
Your marketing site |
product |
Your web app |
mobile |
Your React Native app |
This means mobile activity is separated from web activity everywhere it matters: you can filter the Errors page by Mobile, scope a dashboard block to Mobile, and compare mobile against web without one polluting the other.
There Is No URL
A native app has no page address, so mobile events differ from web events in two ways that are worth knowing:
urlis empty. Screen identity lives in ascreen_nameproperty instead.hostnameis your app’s bundle identifier (com.acme.app) rather than a domain. For a project with more than one app, this is how you tell them apart.
Overcentric deliberately does not invent a fake URL like screen://Checkout for mobile screens. Doing so would mix unreachable entries into your website page reports and split a single crash into a separate error group for every screen it happened on.
Screen Tracking
React Navigation
Import the adapter and pass its three values to your NavigationContainer:
import { NavigationContainer } from "@react-navigation/native";
import { useOvercentricNavigation } from "overcentric-react-native/navigation";
function App() {
const { ref, onReady, onStateChange } = useOvercentricNavigation();
return (
<NavigationContainer ref={ref} onReady={onReady} onStateChange={onStateChange}>
<RootStack />
</NavigationContainer>
);
}onReady is required, not optional. onStateChange does not fire for the first route, so without onReady you lose the opening screen of every session — usually the most important one.Expo Router, or Anything Else
Call trackScreen yourself:
import { usePathname } from "expo-router";
import { trackScreen } from "overcentric-react-native";
export default function Layout() {
const pathname = usePathname();
useEffect(() => {
trackScreen(pathname);
}, [pathname]);
return <Slot />;
}trackScreen ignores consecutive calls with the same name, so it is safe to call from a render path.
Identifying Users
overcentric.identify("user_42", {
email: "ada@acme.com",
name: "Ada Lovelace",
company: { id: "acme", name: "Acme Inc", website: "acme.com" },
});Two differences from the web library, both because mobile sessions are long-lived:
- Identity persists across app restarts. A mobile user logs in once and stays logged in for months, so the SDK stores the identity and attaches it to every event until you clear it. On the web, identity is re-established on each page load.
- Traits are merged, not replaced. Calling
identify("user_42")on its own will not wipe an email you set earlier.
Call reset() when a user logs out:
await overcentric.reset();This flushes anything pending, clears the identity, and issues a new device ID so later anonymous activity is not attributed to the person who just logged out.
identify() with the same identifier is what connects them into one person.Custom Events
overcentric.track("checkout_completed", { plan: "pro", amount_cents: 4900 });The same reserved event names work here, so $signup and the subscription events feed the same pre-built reports as on web.
What Is Tracked Automatically
| Event | When |
|---|---|
$initial_visit |
The first launch on a device |
$app_open |
Every cold start |
$screen_view |
A screen change |
$error |
An uncaught JavaScript error or unhandled promise rejection |
$identify |
You call identify() |
Pass autoCapture: false to init() to disable all of these except $error and $identify.
Mobile deliberately does not reuse the web’s $page_view, $click, or $scroll names. A screen is not a page, and mixing them would corrupt your existing web reports.
Error Tracking
Captured automatically:
- Uncaught JavaScript errors
- Unhandled promise rejections
Not captured in this version:
- Native iOS and Android crashes
- Failed network requests
- React render errors
The SDK always chains to any error handler already installed, so the development red box and other reporters such as Sentry keep working alongside it.
Repeated errors are deduplicated within a five-second window and capped at 25 per minute, so a render loop cannot flood your dashboard or drain a user’s battery.
Offline Behaviour
Mobile networks fail constantly, so the SDK queues events rather than sending them one at a time:
- Sent every 15 seconds while the app is open, and once when it is backgrounded
- The queue is saved to disk, so events survive the app being force-quit
- Up to 1000 events are held; past that, the oldest are dropped
- A network failure never discards an event — only a rejection from the server does
Expect a short delay before events appear in your dashboard. Call flush() to send immediately.
To make the queue survive restarts, install a storage package:
npx expo install @react-native-async-storage/async-storageWithout one, events are kept in memory only and are lost when the app closes. The SDK logs a warning once when this happens.
Optional Packages
The SDK has no required dependencies. These add capability if present:
| I want | Install |
|---|---|
| Events to survive app restarts | @react-native-async-storage/async-storage |
| Best reliability, especially for crashes | react-native-mmkv |
| Automatic screen tracking | @react-navigation/native |
| Device model and app version in reports | react-native-device-info, or expo-device + expo-application |
Without the device packages, device_model and app_version are empty. Everything else still works.
Session Replays Are Not Available
Replays continue to work normally for your website and product. The Replays page has no Mobile filter, because there is nothing to show there.
Configuration
| Option | Default | What it does |
|---|---|---|
projectId |
— | Required. Your project ID |
basePath |
https://app.overcentric.com/api |
API base URL |
debugMode |
false |
Verbose logging |
autoCapture |
true |
Emit $app_open, $initial_visit, $screen_view |
enableErrorCapture |
true |
Install the global error handlers |
flushIntervalMs |
15000 |
How often queued events are sent |
maxQueueSize |
1000 |
Events held while offline |
sessionTimeoutMs |
1800000 |
Idle time before a new session starts |
beforeSend |
— | Change or drop events; return null to drop |
API Reference
init(options): Promise<void>
track(name, properties?): void
trackScreen(name, properties?): void
identify(uniqueIdentifier, info?): void
reset(): Promise<void>
flush(): Promise<void>
optOut(): Promise<void>
optIn(): Promise<void>
isOptedOut(): boolean
isInitialized(): boolean
getDeviceId(): string | null
getSessionId(): string | null
setLogLevel(level): voidEvery method is safe to call before init() finishes — calls are buffered and sent once it is ready.
Privacy
The SDK collects the platform, OS version, device model, app version, screen size, a random device ID, and whatever you pass to track() and identify().
It never collects advertising identifiers. Because it does not touch IDFA, adding Overcentric will not force your app into an App Tracking Transparency prompt. It also never reads the MAC address, IMEI, phone number, contacts, location, clipboard, screenshots, or keystrokes.
Your users’ IP addresses are not sent by the SDK. Overcentric derives an approximate country from the connection and stores only the country code.
To stop collection entirely:
await overcentric.optOut();This is remembered across restarts and discards anything still queued. Pass optOut: true to init() if you would rather start with collection off and enable it after asking for consent.
Troubleshooting
No events appear. Turn on debugMode: true and read the logs. The usual causes are a wrong project ID, or a physical device that cannot reach a development server on localhost — use your machine’s network IP instead.
“No persistent storage found” warning. No storage package is installed. See Offline Behaviour.
Device model and app version are empty. Install react-native-device-info, or expo-device and expo-application.
Events are slow to appear. That is the queue working as designed. Call flush() to send immediately.