Feature Flags

Read FastStats feature flags in the browser with the built-in SDK

Feature Flags are still in Private Preview. Contact staff if you would like to try them out.

The browser SDK loads your project’s flags for the visitor it already tracks and reads them synchronously from memory. There is nothing to list up front: add the extension and read any flag by key.

Enable Flags

Add the featureFlags() extension next to your other extensions.

app/faststats.tsx
"use client";

import { Analytics } from "@faststats/react";
import { featureFlags } from "@faststats/react/flags";

const flags = featureFlags();

export function FastStats() {
	return <Analytics siteKey="your_site_key" extensions={[flags]} />;
}

Create the extension once, at module scope, so every part of the app reads the same instance.

Read Flags

Every read takes the flag key and a default. The default is served until flags have loaded, and whenever the flag is missing, archived, or has a different type than the default.

import { useFlag } from "@faststats/react/flags";

export function Checkout() {
	const newCheckout = useFlag("new-checkout", false);
	const maxUploads = useFlag("max-uploads", 10);

	return newCheckout ? <NewCheckout limit={maxUploads} /> : <OldCheckout />;
}

useFlag returns the default during server rendering and until flags have loaded, then re-renders with the flag’s value.

MethodReturns
get(key, default)The flag’s value, or the default
isEnabled(key)true when a boolean flag is on
details(key){ value, variant, reason, ruleId? }, or undefined if not served
status"idle", "loading", "ready", or "error"
subscribe(listener)Calls listener when values or status change; returns an unsubscribe

reason explains the result: rule_match, target_match, fallthrough, disabled, or prerequisite_failed. In React, useFeatureFlags() returns the running instance for status, details(), setContext(), and reload().

Exposures

A flag counts as exposed the first time your code reads a served value for a visitor, not when it loads. The exposure numbers on a flag’s dashboard page, its evaluation log, and per-variant breakdowns in insights all come from these reads, so they show the people who actually saw a variant.

  • Only the first read of each flag and variant is reported per visitor.
  • Reads close together are sent in one small request, and anything left is sent when the page is hidden.
  • Reading a default, while loading or for a missing flag, is not an exposure.

Read a flag where you branch on it, not ahead of time, so exposures match what people see.

Targeting

Flags are evaluated for the anonymous visitor ID, and the session ID links each evaluation to analytics. The service also derives browser, browser_version, os, os_version, device, and country from the request.

Pass anything else your targeting rules match on as attributes:

const flags = featureFlags({
	attributes: { plan: "pro", beta: true },
});

// Later, for example after the user upgrades. Replaces the previous attributes.
await flags.setContext({ attributes: { plan: "enterprise" } });

Signed-in Users

identify() re-evaluates flags for that user, so percentage rollouts follow the user across devices. logout() goes back to a new anonymous visitor. While flags reload for a new user, reads serve their defaults rather than the previous user’s values.

When the user is already known at startup, pass userId to skip the anonymous load:

const flags = featureFlags({ userId: session.user.id });

Flags are not loaded while consent is "denied". Revoking consent clears loaded values and drops exposures that were not sent yet. A cookieless visitor has no lasting ID, so rollouts hold for the session only.

Options

OptionTypeDefaultDescription
flagsstring[]all flagsLoads only these keys, for projects with many flags
userIdstringnoneYour ID for a user already signed in at startup
attributesRecord<string, unknown>{}Anything targeting rules should match on
endpointstringhttps://flags.faststats.devBase URL of the flags service
timeoutMsnumber3000Aborts a slower load, so flags serve their defaults
fetchtypeof fetchglobal fetchA fetch implementation to use instead

flags only limits what is loaded. Exposures are recorded for reads either way.