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

Evaluate feature flags

Evaluate feature flags and variants for the current visitor in the browser.

The web SDK evaluates the feature flags you configure in Studio for the current visitor, so a feature can be on for some people and off for others without a deploy. Create and roll out flags under A/B testing → Feature flags; see Feature flags.

Early access

Feature flags are in early access. Ask us on Discord, or email support@voidhash.com on a paid plan, to turn them on for your team.

Evaluate flags

getFeatureFlags() evaluates the keys you pass, or every flag when you pass none:

const { flags, isStale } = await voidhash.getFeatureFlags(["new-pricing", "checkout-test"]);
const newPricing = flags.find((flag) => flag.key === "new-pricing")?.enabled ?? false;

Each entry has key, enabled and variantKey, which is null for a flag without variants. payload is always null: flag payloads are not delivered to browsers.

After an evaluation, read single flags synchronously:

const enabled = voidhash.isFeatureEnabled("new-pricing");
const variant = voidhash.getFeatureVariant("checkout-test")?.variantKey;

A flag that is turned off, unknown or not evaluated yet reads as its default.

Defaults

A flag reads as its default until Voidhash has evaluated it: before the first answer, and when Voidhash is unreachable and nothing is cached. Without a default it reads as false. Give a flag whose feature must keep working during an outage the default true:

import { createVoidhashClient } from "@voidhash/web";

const voidhash = createVoidhashClient({
  featureFlags: { defaults: { "new-checkout": true } },
  publishableKey: "vh_pk_...",
});

Defaults apply to isFeatureEnabled() and to isEnabled() from useFeatureFlags(). The flags from getFeatureFlags() hold evaluated values only. isFeatureEnabledByDefault(key) returns a flag's default.

In React

useFeatureFlags() evaluates the keys you pass and re-renders when their values change. It compares keys by value, in any order:

import { useFeatureFlags } from "@voidhash/web/react";

const FLAG_KEYS = ["new-pricing", "checkout-test"];

export function Pricing() {
  const { getVariant, isEnabled, isLoading } = useFeatureFlags(FLAG_KEYS);
  if (isLoading) return null;

  return (
    <PricingTable
      layout={isEnabled("new-pricing") ? "new" : "classic"}
      checkoutVariant={getVariant("checkout-test")?.variantKey ?? undefined}
    />
  );
}
ValuePurpose
data.flagsEvery evaluated entry.
isEnabled(key)Whether the flag is enabled; its default while unknown or loading.
getVariant(key)The flag's entry, or null while unknown or loading.
isLoadingtrue until the first evaluation answers, at most maxWaitMs with nothing cached.
isStaletrue when values came from the cache without a successful refresh.
isExpiredtrue when there are no evaluated values: before the first answer, or when Voidhash is unreachable and nothing is cached.
errorWhy the client failed to start or a refresh failed, or null. Network slowness does not set it.
refetch()Evaluates again from the network.

Caching and refresh

Flags are read from the cache first, so a cached value never waits long for the network:

  • Values older than featureFlags.ttlMs, 5 minutes by default, are refreshed. A read waits at most 500 ms for the refresh, then answers from the cache with isStale: true.
  • With nothing cached, a read waits up to featureFlags.maxWaitMs, 2 seconds by default, then resolves with no flags and isExpired: true. The evaluation continues in the background, and its answer is cached and announced with the feature-flags-updated event. A read never rejects because of the network, so check isExpired to tell that you are seeing defaults.
  • The cache lives in localStorage so the next page load starts with the last values. featureFlags: { persist: false } keeps it in memory. With storeBeforeConsent: false, flags stay in memory until consent is granted.
  • Flags refresh when the browser reconnects and when the tab becomes visible, at most once a minute.
  • identify(), signOut() and reset() evaluate flags again for the new person. After a sign-in, the anonymous visitor's values answer as stale until the user's arrive. signOut(), reset() and a switch between users clear the cache, so one person never sees another's assignment.

Feature flags work without analytics consent.

Website experiments

A flag with variants assigns each visitor a stable variant, which you can use for an A/B test on your website. The web SDK records no exposure event when a flag is read, so track your own event with the variant when the visitor sees it. A/B tests in Studio run through paywall locations in the mobile SDKs, not the web SDK.

Choose a safe default for every flag. Do not gate paid access with a flag; check access on your server instead.

Next steps

  • Create and roll out flags in Studio
  • Check access from your backend with Node.js, PHP, Go, Rust or over HTTP
  • Track events

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

PreviousIdentify usersNextTrack events

On this page

Evaluate flagsDefaultsIn ReactCaching and refreshWebsite experimentsNext steps