Quickstart
Go from an installed SDK to a live paywall and entitlement check in four steps.
This page is the compressed path through the SDK: create the client, wrap your app, show a paywall, check access. Each step links to a deeper page that covers the full API surface.
Prerequisites
Before you write any code, make sure you have:
- A Voidhash project in the dashboard and its publishable key (
vh_pk_...). - At least one product configured in your project.
- A paywall published to a location (this guide uses
onboarding). - The SDK installed and
voidhash-cli initrun in your app — see Installation and voidhash-cli init.
Typed slugs are optional but recommended
Run voidhash-cli types generate to create a voidhash.gen.d.ts file that turns product,
location, and perk slugs into literal string unions. Without it every slug is a plain string —
everything still works, you just lose autocomplete. See
Schema & Types.
Create the client
createVoidhashClient takes your publishable key and returns a bag containing the Provider component and every hook, all bound to a single client instance. Create it once in a shared module and export the whole bag:
import { createVoidhashClient } from "@voidhash/react-native";
export const voidhash = createVoidhashClient("vh_pk_...");There is no schema argument — your schema lives on the server and is fetched when the Provider mounts.
A deep-link scheme is required
The client needs a deep-link scheme for purchase callbacks. In an Expo app it is read
automatically from scheme in your app config; in a bare React Native app pass it explicitly via
createVoidhashClient("vh_pk_...", { scheme: "myapp" }). If no scheme can be resolved, the call
throws a SchemeNotSetError.
The options object also accepts baseUrl, debug, readOnly, and more — see Configuration.
Wrap your app in the Provider
Mount voidhash.Provider at the root of your app. On mount it initializes the client: it establishes the person's identity, fetches your schema, and starts observing store transactions. All hooks wait for initialization automatically.
import { Stack } from "expo-router";
import { voidhash } from "../lib/voidhash";
export default function RootLayout() {
return (
<voidhash.Provider>
<Stack />
</voidhash.Provider>
);
}Show a paywall
usePaywallByLocation resolves the paywall assigned to a location and preloads it into a native full-screen WebView, so it opens instantly. Call show() to present it:
const paywall = voidhash.usePaywallByLocation("onboarding");
await paywall.show();show() resolves to false when the location has no published paywall, so you can fall back gracefully. Purchases and restores triggered inside the paywall are handled end to end by the SDK — the paywall dismisses itself after a successful purchase. Hook options (onPurchase, onRestore, onError) let you react to those outcomes; see Displaying Paywalls.
Check access
useCurrentPerson returns the current person, including their entitlement grants. A perk is unlocked when a grant for it is active:
const { data, isLoading } = voidhash.useCurrentPerson();
const hasPremium = data.entitlements?.grants.some(
(grant) => grant.perkId === "premium" && grant.status === "active",
);data is an empty object until the person has loaded, so keep the optional chaining. The hook is reactive: the SDK refreshes the person after a successful purchase or restore, so hasPremium flips without any manual refetching. More in Subscription Status.
Full example
Everything together in one gated screen:
import { Button, Text, View } from "react-native";
import { voidhash } from "../lib/voidhash";
export default function PaywallGate() {
const { data, isLoading } = voidhash.useCurrentPerson();
const paywall = voidhash.usePaywallByLocation("onboarding", {
onPurchase: ({ productId }) => {
console.log(`Purchased ${productId}`);
},
onError: (error, { action }) => {
console.warn(`Paywall ${action} failed`, error);
},
});
const hasPremium = data.entitlements?.grants.some(
(grant) => grant.perkId === "premium" && grant.status === "active",
);
if (isLoading) {
return null;
}
if (hasPremium) {
return (
<View>
<Text>Premium is active. Enjoy!</Text>
</View>
);
}
return (
<View>
<Text>Unlock premium to continue.</Text>
<Button
title="See plans"
onPress={async () => {
const shown = await paywall.show();
if (!shown) {
console.warn("No published paywall for the onboarding location");
}
}}
/>
</View>
);
}Next steps
- Displaying Paywalls — preloading behavior, paywall callbacks, and locations in depth.
- Purchases — trigger purchases from your own UI with
usePurchase. - Subscription Status — grants, the current subscription, and purchase history.
- Identifying Users — attach purchases to your own user IDs with
identify. - Configuration — every
createVoidhashClientoption, including read-only observer mode.