React Native quickstart
Add Voidhash to an Expo or React Native app, sell a product and unlock a perk.
Install @voidhash/react-native, sell pro_monthly from your own screen, and unlock the premium
perk with a test purchase. Development mode lets you finish before you connect the App Store or
Google Play.
Before you start
You need:
- An Expo app with development builds or a bare React Native app. Expo Go can't load the SDK's native code.
- A Voidhash project with a perk
premiumand a subscription productpro_monthlythat unlocks it. Give the product a billing duration; store mapping can wait. See Products and perks. - Both publishable keys from Settings → API keys: production (
vh_pk_...) and test (vh_pk_test_...). See API keys.
Set up
Install the SDK
Install the SDK from the Voidhash package registry, with the exact peer versions it supports:
npm install @voidhash/react-native@https://pkg.voidha.sh/@voidhash/react-native/branch:main react-native-nitro-modules@0.37 effect@4.0.7The SDK requires effect 4.0.7 exactly and react-native-nitro-modules 0.37.x. With pnpm, add
blockExoticSubdeps: false to pnpm-workspace.yaml first: the SDK's native sources also install
from the registry, and recent pnpm versions block that. To pin a build, see
Install.
In an Expo app, register the config plugin. It adds the iOS pods when you prebuild.
{
"expo": {
"plugins": ["@voidhash/react-native"]
}
}In a bare React Native app, add the pods inside your app target in ios/Podfile, above
use_native_modules!, then run npx pod-install:
voidhash_ios = File.dirname(`node --print "require.resolve('@voidhash/ios/package.json', { paths: [require.resolve('@voidhash/react-native/package.json')] })"`.strip)
pod "VoidhashCore", :path => voidhash_ios
pod "Voidhash", :path => voidhash_iosAndroid needs no extra setup.
Configure the client
Create one client at module scope. Debug builds use the test key with development mode, so purchases go through a simulated store; release builds use the production key and the real store.
import { createVoidhashClient } from "@voidhash/react-native";
export const voidhash = createVoidhashClient(__DEV__ ? "vh_pk_test_..." : "vh_pk_...", {
// Simulated purchases in debug builds; release builds ignore it.
dev: true,
});Mount the provider once at the app root:
import { Stack } from "expo-router";
import { voidhash } from "../src/lib/voidhash";
export default function RootLayout() {
return (
<voidhash.Provider>
<Stack />
</voidhash.Provider>
);
}Show products and buy
Load the product and buy it with usePurchase:
import { useState } from "react";
import { Button, Text, View } from "react-native";
import { voidhash } from "../src/lib/voidhash";
export default function UpgradeScreen() {
const { data: products, isLoading } = voidhash.useProducts();
const { purchase, isLoading: isPurchasing } = voidhash.usePurchase();
const [message, setMessage] = useState<string>();
const monthly = products.get("pro_monthly");
if (isLoading) return null;
if (!monthly) return <Text>Product unavailable</Text>;
return (
<View>
<Button
disabled={isPurchasing}
title={`Subscribe for ${monthly.displayPrice}`}
onPress={async () => {
const result = await purchase(monthly);
if (result.isErr()) setMessage(`Purchase failed: ${result.error.message}`);
else if (result.value.status === "pending") setMessage("Waiting for approval.");
else if (result.value.status === "deferred")
setMessage("Purchase received. Unlocking soon.");
else setMessage(undefined);
}}
/>
{message ? <Text>{message}</Text> : null}
</View>
);
}purchase() never rejects; it resolves to a Result. A closed store sheet is cancelled, not an
error. pending waits for approval, such as Ask to Buy, and deferred means the store charged but
Voidhash hasn't confirmed yet. In both cases the SDK unlocks the perk once the purchase completes. See
Make and restore purchases.
Check access
Gate premium content on the perk:
import { Text } from "react-native";
import { voidhash } from "../src/lib/voidhash";
export default function PremiumScreen() {
const { hasAccess, isLoading } = voidhash.useHasPerk("premium");
if (isLoading) return null;
return <Text>{hasAccess ? "Premium unlocked" : "Upgrade to unlock"}</Text>;
}The hook re-renders when a purchase or restore grants the perk.
Test a purchase
Rebuild the app, because the SDK contains native code:
npx expo run:iosUse npx expo run:android for Android, or a new EAS development build. Open the upgrade screen and
tap Subscribe. In a debug build with the test key, a confirmation sheet replaces the store sheet
and prices are fixed test prices. After you confirm, the premium screen shows Premium unlocked.
The purchase is a test purchase: it appears in Studio under Test in the environment switcher
and never unlocks production. See Development mode.
Buy through the store (optional)
Once the simulated purchase works, test the real store sheet. Test accounts aren't charged.
- App Store: connect the App Store (Help center) and map
pro_monthlyto your App Store product. Run a release build (npx expo run:ios --configuration Release) on a device signed in to a Sandbox Apple Account, or install it from TestFlight. - Google Play: connect Google Play (Help center) and map the product. Upload a release build to an internal test track and install it from Google Play as a license tester.
Release builds use the production key and the real store. The purchase appears in Studio under Sandbox. See Test purchases (Help center).
Next steps
- See a complete sample app: products, restore, a paywall with a fallback screen, sign-in and a feature flag.
- Identify signed-in users
- Look up options, hooks and methods
- Show a paywall designed in Studio
Something wrong or missing on this page? Tell us by email or on Discord.