Feature flags
Evaluate remotely configured flags and variants for the current visitor.
Feature flags turn features on or off, and pick variants, without a new deploy. Voidhash evaluates every flag for the current visitor, so the same flag can resolve differently for different people. Configure flags in Studio under Feature flags.
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 in the web SDK; flag payloads are not delivered to browsers yet.
After an evaluation, read single flags synchronously:
const enabled = voidhash.isFeatureEnabled("new-pricing");
const variant = voidhash.getFeatureVariant("checkout-test")?.variantKey;Unknown flags, and flags not evaluated yet, are disabled.
In React
useFeatureFlags evaluates the keys you pass and re-renders when they change. It compares keys by
value, in any order, so it evaluates again only when the set of keys changes:
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;
const checkout = getVariant("checkout-test");
return (
<PricingTable
layout={isEnabled("new-pricing") ? "new" : "classic"}
checkoutVariant={checkout?.variantKey ?? undefined}
/>
);
}| Value | Purpose |
|---|---|
data.flags | Every evaluated entry. |
isEnabled(key) | Whether the flag is enabled. Unknown and still-loading flags return false. |
getVariant(key) | The flag's entry, or null while it is loading or unknown. |
isLoading | true until the first evaluation answers, including while the provider starts; false when the client failed to start. |
isStale, isExpired | true when the values came from the cache without a successful refresh. |
error | The error, when there was nothing to answer with or the client failed to start, or null. |
refetch() | Evaluates again from the network. |
Caching and refresh
Flags are read from the cache first, so a cached value never waits for the network:
- Fresh values are answered from the cache. Values older than
featureFlags.ttlMs, five minutes by default, are refreshed; the read waits at most 500 ms for the refresh, then answers from the cache withisStale: true. - Without any cached value, the first read waits for Voidhash, at most 10 seconds, and then answers with every flag disabled.
- The cache is kept in
localStorage, so the next page load starts with the last values. Turn this off withfeatureFlags: { persist: false }, which keeps flags in memory for the page. - Flags are refreshed when the browser reconnects and when the tab becomes visible, at most once a minute.
identify(),signOut(), andreset()clear the cached flags and evaluate them again for the new person, so one visitor never sees another visitor's assignment.
Feature flags keep working while analytics consent is not granted.
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 does not record an exposure event when a flag is read; to analyze a test, track an event of your own with the variant when the visitor sees it.
Choose a safe default for a flag that is loading or unavailable. Do not use a feature flag to gate paid access; check access on your server with the Node.js library.