React Native SDK
Install @voidhash/react-native, set up its native code and create the client.
@voidhash/react-native adds paywalls, purchases, perks, feature flags and analytics to Expo
development builds and bare React Native apps on iOS and Android. It runs on the same native core
as the Swift and Kotlin SDKs, so purchases, caching and queues behave the same on every platform.
Start with the quickstart. Options, hooks and methods are in the
React Native SDK reference.
Install
@voidhash/react-native installs from the Voidhash package registry, pkg.voidha.sh. branch:main names the
newest build:
npm install @voidhash/react-native@https://pkg.voidha.sh/@voidhash/react-native/branch:main react-native-nitro-modules@0.37 effect@4.0.7With pnpm, add blockExoticSubdeps: false to pnpm-workspace.yaml before you install. The SDK's
native sources, @voidhash/ios and @voidhash/android, also install from the registry, and recent
pnpm versions block dependencies that install from a URL.
To pin a build, install from https://pkg.voidha.sh/@voidhash/react-native/<hash>, using the short commit hash at
the end of the installed version, such as 83f7412 in 0.0.1-alpha.1-g83f7412.
The SDK is an alpha release and isn't on npm yet: npm install @voidhash/react-native installs an
older preview that doesn't match these docs. Builds are kept for three weeks, so pin a recent one
and update regularly. See Versions and releases.
Then set up the native code as in Native setup, and rebuild the app.
Compatibility
| Requirement | Version |
|---|---|
| iOS | 15 or later |
| Android | minSdkVersion 23, compileSdkVersion 35 or later |
| React Native | Verified on 0.83, developed against 0.81 |
| Expo SDK | Verified on 55, developed against 54. Optional for bare apps. |
effect | Exactly 4.0.7 (peer dependency) |
react-native-nitro-modules | 0.37.x, ^0.37.1 (peer dependency) |
| Google Play Billing Library | 9, added by the SDK |
react-native-iap, if used | 16.6.2 or later |
react and react-native are peer dependencies. expo, expo-router and
@react-navigation/native are optional peers used only by the config plugin and the
screen-tracking integrations.
Every Nitro-based module in the app shares one native runtime. Mixing Nitro minors across modules causes native build or load failures that don't point at Voidhash. Upgrade them together.
On Android, Gradle resolves one Billing Library version for the whole app. A billing library in your app that still calls APIs removed in Billing 8 fails at runtime, so upgrade it before you add the SDK.
The package ships native code. Changing its version needs a new native build: an over-the-air
update can't deliver it, and must not roll an app back to an earlier SDK version. A JavaScript
bundle running on a binary without the SDK's native modules, such as Expo Go, still loads, but
init() fails with FAILED_TO_INITIALIZE_VOIDHASH_CLIENT.
Native setup
| Setup | Expo | Bare React Native |
|---|---|---|
iOS pods VoidhashCore, Voidhash | Config plugin @voidhash/react-native | Podfile lines, see the quickstart |
| Paywall renderer | Built from source, then plugin option { "renderer": true } | Built from source, then the VoidhashUI pod and Voidhash_renderer=true in android/gradle.properties |
| Android | Nothing else | Nothing else |
The renderer is off by default, so apps that don't show paywalls don't ship it. Without it,
presentFlow() fails with FLOW_RENDERER_MISSING. The published packages don't include it yet; see
Add the paywall renderer. After changing
plugin options, run npx expo prebuild and rebuild.
On Android, the SDK adds a background job service and the RECEIVE_BOOT_COMPLETED permission to
your manifest; see unacknowledged purchases.
Create the client
Create one client at module scope and keep it for the life of the app:
import { createVoidhashClient } from "@voidhash/react-native";
export const voidhash = createVoidhashClient(__DEV__ ? "vh_pk_test_..." : "vh_pk_...", {
dev: true,
});Release builds, including TestFlight and Play test tracks, use the production key; debug builds use the test key. See API keys and Environments. Never ship a secret key.
A second client for the same key and base URL replaces the first when it initializes; development
builds warn about it. Clients for different keys or base URLs run side by side on iOS. Android runs
one client per process: a client that initializes there ends the one that was running, so call
end() on one before you start another.
Then mount <voidhash.Provider> above your app. It starts the client and gives hooks such as
voidhash.useHasPerk their data. See Client options
and Provider.
Generated slug types
voidhash types generate writes voidhash.gen.d.ts, which types product, perk and location slugs
for the SDK. Without it, slugs are plain strings and everything else works the same. The CLI isn't
published to a package registry yet; Install the CLI explains how to build it. Run
it after you change slugs in Studio, or regenerate on the fly with the Metro plugin:
const { getDefaultConfig } = require("expo/metro-config");
const { withVoidhash } = require("@voidhash/react-native/metro");
module.exports = withVoidhash(getDefaultConfig(__dirname));While the Metro dev server runs, it runs voidhash-cli types generate --watch, polling every
5,000 ms. Bundling commands such as expo export start nothing. The binary must be on PATH, or
set cliBinary to the built CLI; without it Metro logs a warning and keeps running.
| Option | Default | Description |
|---|---|---|
pollIntervalMs | 5000 | How often to check for schema changes. |
cliBinary | voidhash-cli | Path to the CLI binary. |
extraArgs | [] | Extra arguments after types generate --watch. |
Data collection
Use this table for the App Store privacy details and the Google Play Data safety form.
| Data | When | Used for |
|---|---|---|
| Device ID | Always: an anonymous ID per install | App functionality, analytics |
| User ID | When you call identify() | App functionality, analytics |
| Email address and name | When you pass them to identify() or as attributes | App functionality |
| Purchase history | Always: the store transactions the SDK observes | App functionality, analytics |
| Product interaction | With analytics consent: events, lifecycle events, screens | Analytics |
| Advertising data | With analytics consent, unless installAttribution is off: the campaign, ad and click IDs behind the install | Analytics |
| Crash data | With analytics consent: that the app crashed, and in which version | Analytics |
| Coarse location | With analytics consent: city-level, derived from the request address | Analytics |
Requests also carry the device model, OS version, app version and locale. Data is linked to the person. The SDK doesn't read advertising identifiers or share data with other companies for tracking.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.