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 init run 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:

lib/voidhash.ts
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.

app/_layout.tsx
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:

app/paywall-gate.tsx
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 createVoidhashClient option, including read-only observer mode.