React Native SDK reference
Client options, provider state, hooks, client methods, caching and diagnostics of @voidhash/react-native.
Everything createVoidhashClient accepts and the client exposes. To install the SDK, see
React Native SDK.
Client options
| Option | Default | Description |
|---|---|---|
dev | false | Simulated purchases in debug builds; ignored in release builds. See Development mode. |
debug | false | Logs HTTP requests and responses, with sensitive values redacted. |
enabled | true | false makes the client inert. Fixed at construction. See Disabled clients. |
readOnly | false | Observer mode. See Observer mode. |
distinctId | Stored or new anonymous ID | Seeds the identity on the first init() only. Usually omit it and call identify(). |
analyticsConsent | "granted" | "denied" or "pending" stops analytics. See Analytics consent. |
automaticLifecycleEvents | true | Captures $app_* events and $sign_out. |
installAttribution | true | Credits installs to the ad behind them. See Install attribution. |
metaAppId | none | Android only: also asks Meta's apps about the install. |
screenTracking | { enabled: true } | Automatic $screen events. See Track screens. |
flowCapabilities | ["links"] | What paywalls may do beyond purchasing. See Flow capabilities. |
preloadPlacements | [] | Location slugs whose paywalls download at launch. |
onDiagnostic | none | Receives failures the SDK recovered from. See Diagnostics. |
baseUrl | https://api.voidhash.com | API origin for self-hosted deployments. |
ingestUrl | Origin of baseUrl | Analytics origin only. |
baseUrl and ingestUrl must be absolute http:// or https:// URLs; blank counts as unset. For
an invalid URL, development builds throw INVALID_ARGUMENT; release builds use the default origin
and report INVALID_CONFIGURATION through onDiagnostic.
scheme, connectivity and unstable_nativeEngine are deprecated and have no effect.
unstable_swallowErrors is deprecated; use onDiagnostic.
Disabled clients
With enabled: false the client never touches the store or the network. Methods resolve as no-ops
and reads return their empty shape. Mount the provider either way, so hook order matches in
flagged-off and flagged-on builds. To enable the SDK later, create a new client.
Observer mode
With readOnly: true the SDK records purchases that another billing SDK makes, but never starts,
finishes or acknowledges them. purchase() and setPersonAttributesSync() return
READ_ONLY_PURCHASE_NOT_ALLOWED; everything else works. Switch at runtime without a new client:
voidhash.client.setReadOnly(false); // Voidhash now owns purchases
voidhash.client.isReadOnly; // falseA purchase already in flight keeps the mode it started in. Report the other SDK's purchases with
reportTransaction. See
Observer mode for the migration plan.
Provider
Mount <voidhash.Provider> above your app. It initializes identity, the project schema, the
person, the store connection and the transaction observer. With several React roots, mount a
provider in each; they share one client, which stops when the last provider unmounts.
useVoidhash() returns the provider state:
| Field | Type | Description |
|---|---|---|
status | "initializing" | "ready" | "failed" | "disabled" | Lifecycle state. |
initError | VoidhashError | null | Set only while status is failed. |
retryInit | () => void | Runs init() again. Does nothing unless status is failed. |
isInitialized | boolean | status === "ready". |
client | VoidhashClient | The imperative client. |
Initialization doesn't wait for the network: an offline cold start initializes from device state.
It fails when the binary lacks the SDK's native modules, so handle failed:
const { status, initError, retryInit } = voidhash.useVoidhash();
if (status === "failed") return <RetryScreen error={initError} onRetry={retryInit} />;On iOS, an app launched in the background before the first unlock after a reboot stays
initializing until the device is unlocked. Events captured meanwhile are kept.
Hooks
Hooks are client properties, such as voidhash.useHasPerk. Data hooks report isLoading: true
until the provider initializes and their first read answers, and false for a disabled or failed
provider. error is null when there is none. After identify() or reset(), mounted hooks
read again for the new person.
| Hook | Returns | Guide |
|---|---|---|
useHasPerk(perkSlug) | { hasAccess, grant, isLoading, isStale, error, refetch } | Check access |
useCurrentPerson() | { data, error, isLoading, refetch } | Check access |
usePresentFlow(placement) | { present, isPresenting } | Display a paywall |
useProducts() | { data: { get, toList, bySlug }, error, isLoading, refetch } | Purchases |
usePurchase(options?) | { purchase, isLoading, error } | Purchases |
useFeatureFlags(keys?) | { isEnabled, getVariant, data, error, isLoading, refetch } | Feature flags |
useVoidhash() | Provider state | Provider |
Client methods
voidhash.client exposes the same workflows outside React. Methods that can fail resolve to a
Result and never reject; see Errors.
| Method | Description |
|---|---|
hasPerk(slug, { forceFetch? }) | Checks one perk, with freshness. |
getCurrentPerson({ forceFetch? }) | Returns { person, isStale, isExpired, reason }. |
getProducts() | Products by slug. |
purchase(product, { offerId? }) | Buys a product, optionally an offer. |
restorePurchases() | Restores purchases for the current person. |
syncPurchases() | Scans the store and sends missing receipts without prompting. |
reportTransaction(transaction, { isRestore? }) | Reports another SDK's purchase in observer mode. |
presentFlow(placement) / dismissFlow() | Presents or closes the paywall at a location. |
getPaywallForLocation(slug) | What is shown at a location, or null, without presenting it. |
getFeatureFlags(keys?) | Evaluates flags. |
identify(userId, { email?, name? }) / identifySync(...) | Identifies the person. |
signOut() / reset() | Starts a new anonymous identity. |
getDistinctId() | The current distinct ID. |
setPersonAttributes(attrs) / setPersonAttributesSync(attrs) | Sets person attributes. |
setAdDataSharing(granted) | Records consent to share purchases with ad platforms. |
capture(name, props?) / screen(name, props?) | Records an event or a screen. |
trackPushOpened(data) | Records an opened Voidhash notification. |
setAnalyticsConsent(consent) / getAnalyticsConsent() | Sets or reads analytics consent. |
getSessionId() | The current analytics session ID. |
flush() | Sends queued writes, receipts and events now. |
resetCache() | Clears the cached person; the next read fetches it. |
setReadOnly(readOnly) | Switches observer mode. |
iosPresentCodeRedemptionSheet() / iosShowManageSubscriptions() | Apple's offer-code and subscription sheets. iOS only. |
init() / end() | Starts or stops the client. The provider calls both for you. |
end() flushes for at most about 2 seconds, saves what's still queued and stops every timer and
listener; the queue is sent after the next init(). Events captured after end() are dropped.
Package entry points
| Import | Use |
|---|---|
@voidhash/react-native | Client, provider, hooks, types, isAnonymousDistinctId |
@voidhash/react-native/expo-router | ScreenTracking, useExpoRouterScreenTracking |
@voidhash/react-native/react-navigation | useScreenTracking |
@voidhash/react-native/metro | withVoidhash for Metro |
@voidhash/react-native/app.plugin.js | The Expo config plugin |
Caching and offline behavior
Reads answer from the device cache first and refresh in the background, so they don't fail offline.
| Data | Fresh for | Kept for |
|---|---|---|
| Person and perks | 5 minutes | 2 days, then isExpired |
| Feature flags | 5 minutes | Until replaced |
| Paywall per location | 1 hour | 7 days |
A stale read waits at most 500 ms for a refresh, then answers from cache. An uncached read waits for the request, about 10 seconds. The SDK refreshes at launch, on foreground and on reconnect (each at most once a minute), after purchases and restores, and on identity changes.
Events, person writes and store receipts are stored on the device and retried until Voidhash accepts them. Only the event queue is capped: 1,000 events, oldest dropped first. Receipts are never dropped.
Diagnostics
onDiagnostic reports problems the SDK recovered from, for logging and alerts:
import { createVoidhashClient } from "@voidhash/react-native";
createVoidhashClient("vh_pk_...", {
onDiagnostic: (diagnostic) => {
if (diagnostic.kind === "auth") reportToMonitoring(diagnostic);
},
});| Field | Meaning |
|---|---|
kind | transport, eviction, breaker, auth, cache, configuration or consent |
code | Stable code, such as AUTHENTICATION_FAILED or TRANSACTION_REJECTED |
operation | The SDK operation, such as capture |
retryable | Whether the SDK retries on its own |
httpStatus | Present when there was a response |
message | Human-readable detail; don't parse it |
A rejected publishable key is reported once as AUTHENTICATION_FAILED. The refused traffic pauses
and stays queued; one request is let through every 60 seconds.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.