VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Web SDK
Web quickstart
Script tag
Identify users
Evaluate feature flags
Track
Track events
Page views and attribution
Privacy
Privacy and consent
Browser storage
Connect a consent tool
Keep data out of analytics
Revenue and attribution
Sell on the web
Stripe revenue
Web-to-app handoff
Delivery
First-party proxy
Tracking domain
Reference
Web SDK reference
Errors
Troubleshooting

Web SDK reference

Client options, methods, events, React bindings and errors of @voidhash/web.

Everything createVoidhashClient accepts and the client exposes. To install the SDK, see Web SDK.

Client options

OptionDefaultDescription
publishableKeynoneRequired. Your project's vh_pk_... or vh_pk_test_... key.
baseUrlhttps://api.voidhash.comAPI origin: a first-party proxy, tracking domain or self-hosted deployment.
analyticsConsent"granted"Initial consent: "granted", "denied" or "pending". A choice stored by setAnalyticsConsent() wins unless analyticsConsentSource is "app".
analyticsConsentSource"sdk""app" makes your consent tool the only consent source and starts every page at "pending". See Use a consent tool.
storeBeforeConsenttruefalse keeps the SDK's state in memory until consent is granted. See Ask for consent first.
crossSubdomainIdentityfalsetrue shares the anonymous ID across your subdomains in a cookie. See Keep one visitor across subdomains.
distinctIdStored or new anonymous IDStarts the client as this ID. Usually omit it and call identify(). See Seed the identity.
onDiagnosticnoneCalled for every SDK diagnostic. See Diagnostics.
analyticsSee belowPage view and event delivery options.
featureFlagsSee belowFeature flag options.

analytics fields:

FieldDefaultDescription
capturePageViewstrueRecords $pageview on load and after each client-side navigation. Campaign attribution is always recorded.
capturePageLeavescapturePageViewsRecords $pageleave with time on page, scroll depth and Web Vitals. See Page engagement.
captureOutboundClickstrueRecords $outbound_click with the target origin only.
beforeSendnoneFunction, or list of functions, editing or dropping each event. See Edit or drop events.
sanitizeUrlnoneRewrites an address, or returns null to keep a page out. See Keep pages out by address.
enabledtruefalse turns analytics off, as if consent were denied, without storing a choice.
baseUrlClient's baseUrlOrigin for analytics only.
flushIntervalMs5000Send interval for queued events, in milliseconds.
maxBatchSize20Events per request, at most 100. A full batch is sent at once.
maxBatchBytes262144Maximum bytes of events per request. A larger event is sent on its own.
maxQueueSize1000Events kept while offline; the oldest are dropped first.

featureFlags fields:

FieldDefaultDescription
defaultsnoneValues before evaluation, such as { "new-checkout": true }. Others read as false.
maxWaitMs2000How long an uncached read waits for Voidhash, in milliseconds. 0 never waits.
ttlMs300000Age after which cached flags are refreshed, in milliseconds.
persisttrueKeeps flags in localStorage for the next page load.
prefetchOnInitfalseEvaluates every flag in the background during initialize().
refreshOnOnlinetrueRefreshes flags when the browser reconnects, at most once a minute.
refreshOnVisibilitytrueRefreshes flags when the tab becomes visible, at most once a minute.

createVoidhashClient throws VoidhashConfigurationError for a missing or secret key, a non-absolute or non-http(s) URL, an unknown consent value or source, a non-function beforeSend or sanitizeUrl, a size or interval that isn't a positive integer, or non-boolean defaults. Blank strings count as not set.

Client methods

Methods that return a promise wait for a running initialize(). They reject with VoidhashNotInitializedError if it was never called, and with VoidhashDestroyedError after destroy(). Only setPersonAttributesSync() rejects on network failure; everything else queues or answers from the cache.

MethodReturnsDescription
initialize()Promise<void>Loads saved state and starts the SDK. Safe to call more than once.
destroy()Promise<void>Sends what it can, removes listeners and stops the client for good. Called during initialize(), it records no page view.
track(name, properties?, options?)Promise<void>Queues a product event. See Track events.
page(name?, properties?, options?)Promise<void>Records a $pageview yourself. See Page views.
identify(userId, traits?)Promise<void>Switches to your user ID and links the visitor in the background. See Identify users.
signOut()Promise<void>Records $sign_out, then starts a new anonymous identity and session.
reset()Promise<void>Starts a new anonymous identity and session without recording a sign-out.
setPersonAttributes(attributes)Promise<void>Queues person attributes with the events. Needs analytics consent.
setPersonAttributesSync(attributes)Promise<VoidhashPerson>Writes attributes now and returns { personId, distinctId, email, name }. Rejects after 10 seconds without an answer.
getDistinctId()string | undefinedThe current anonymous or identified ID; undefined before initialize() resolves. Put it on payments.
getSessionId()string | undefinedThe session the next event joins; undefined without consent or after 30 idle minutes.
setAnalyticsConsent(consent)Promise<void>Stores and applies the visitor's choice. Works before initialize(). See Privacy and consent.
getAnalyticsConsent()AnalyticsConsentThe consent in effect. Always "denied" while analytics.enabled is false.
setAdDataSharing(granted)Promise<void>Records whether the person agreed to share purchases with ad platforms. See Meta ads.
playStoreUrl(packageName)stringA Google Play link carrying the latest campaign. See Web-to-app handoff.
pauseAnalytics()voidStops recording until resumeAnalytics(). Works before initialize(). See Pause recording.
resumeAnalytics()voidRecords again after pauseAnalytics().
isAnalyticsPaused()booleanWhether recording is paused.
flushAnalytics()Promise<AnalyticsFlushStatus>Sends queued identity links and events, waiting at most 2 seconds. Resolves with { flushed, pending, lastError? }.
getFeatureFlags(keys?)Promise<FeatureFlagsResult>Evaluates flags, cache first. See Feature flags.
refreshFeatureFlags(keys?)Promise<FeatureFlagsResult>Evaluates flags from the network.
isFeatureEnabled(key)booleanThe last evaluated value, or the flag's default. Throws before initialize() and after destroy().
getFeatureVariant(key)FeatureFlagEntry | undefinedThe last evaluated entry; undefined until evaluated. Throws before initialize() and after destroy().
on(event, handler)() => voidSubscribes to a client event. Returns the unsubscribe function.
off(event, handler)voidRemoves a handler added with on().
onDiagnostic(handler)() => voidSame as on("diagnostic", handler).

The React bindings also use three public methods you rarely need:

  • peekDistinctId() returns getDistinctId()'s ID without handing it to your app. That matters only with crossSubdomainIdentity, where an ID your app has read stays on the device.
  • getConfiguredAnalyticsConsent() returns the configured consent, ignoring any stored choice, so a server render and the first client render agree.
  • isFeatureEnabledByDefault(key) returns the flag's featureFlags.defaults entry, or false.

isAnonymousDistinctId(id), exported from @voidhash/web, tells whether an ID is an anonymous vh_anon_... ID.

Client events

EventPayloadWhen
initialized{ distinctId }initialize() has finished.
identity-changed{ distinctId, previousDistinctId }After identify(), signOut() or reset().
analytics-consent-changed{ consent }Consent changed, in this tab or another one.
analytics-flushed{ accepted, rejected, requestId? }A batch was delivered.
analytics-partial-rejection{ accepted, rejected, requestId? }Voidhash refused some events of a batch.
feature-flags-updated{ distinctId, keys?, result }New flag values arrived for distinctId.
diagnosticVoidhashDiagnosticSee Diagnostics.
error{ source, message, error? }A background task failed, such as a scheduled flush.

React

@voidhash/web/react exports a provider and three hooks. This separate entry point is marked "use client"; pages without React never load it.

src/main.tsx
import { VoidhashProvider } from "@voidhash/web/react";
import { createRoot } from "react-dom/client";

import { App } from "./App";

createRoot(document.getElementById("root")!).render(
  <VoidhashProvider config={{ publishableKey: "vh_pk_..." }}>
    <App />
  </VoidhashProvider>,
);

VoidhashProvider takes either config or client, read once on mount:

  • config: the provider creates the client, initializes it on mount and destroys it on unmount. React Strict Mode's remount keeps the same client.
  • client: the provider initializes your client and never destroys it, so code outside React can share it.
HookReturns
useVoidhash(){ client, status, isInitialized, initError, retryInit, distinctId }. status is "initializing", "ready" or "failed"; distinctId follows identity changes.
useAnalytics(){ track, page, flushAnalytics, consent, enabled, setAnalyticsConsent, setAdDataSharing, pauseAnalytics, resumeAnalytics }. Follows consent in other tabs.
useFeatureFlags(keys?){ data, isEnabled, getVariant, isLoading, isStale, isExpired, error, refetch }. See Feature flags.

Hooks throw outside a VoidhashProvider. If initialize() fails, for example because browser storage is blocked, status is "failed", initError says why and retryInit() tries again. Analytics never block rendering, so most apps render normally in every status.

Errors

Rejections are VoidhashError instances exported from @voidhash/web, such as VoidhashNotInitializedError or VoidhashInvalidArgumentError. Background work reports through diagnostics instead. Errors lists every class, code and diagnostic.

Next steps

  • Web SDK
  • Track events
  • Errors

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousTracking domainNextErrors

On this page

Client optionsClient methodsClient eventsReactErrorsNext steps