From 9af7e81bffccdfe8c09f026f4778cade44c51250 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 23 Oct 2026 22:06:03 +0200 Subject: [PATCH] docs: document featureflags from the web perspective --- content/docs/web-analytics/configuration.mdx | 3 +- content/docs/web-analytics/feature-flags.mdx | 137 +++++++++++++++++++ content/docs/web-analytics/meta.json | 3 +- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 content/docs/web-analytics/feature-flags.mdx diff --git a/content/docs/web-analytics/configuration.mdx b/content/docs/web-analytics/configuration.mdx index 0f3c980..6cdfb66 100644 --- a/content/docs/web-analytics/configuration.mdx +++ b/content/docs/web-analytics/configuration.mdx @@ -25,7 +25,8 @@ to the core and all built-in extensions. ## Extensions -Error tracking, Web Vitals, and session replay are separate, tree-shakeable +Error tracking, Web Vitals, session replay, and +[feature flags](/web-analytics/feature-flags) are separate, tree-shakeable imports. Outbound link tracking is part of the core and needs no setup. With React or JavaScript, create each extension and add it to the `extensions` array. diff --git a/content/docs/web-analytics/feature-flags.mdx b/content/docs/web-analytics/feature-flags.mdx new file mode 100644 index 0000000..b1fb4ba --- /dev/null +++ b/content/docs/web-analytics/feature-flags.mdx @@ -0,0 +1,137 @@ +--- +title: Feature Flags +description: Evaluate FastStats feature flags in the browser +icon: Flag +--- + + + Feature Flags are still in **Private Preview** please contact staff if you + would like to try them out. + + +The feature flags extension loads flags for the visitor the analytics client +tracks. After they load, flags are read synchronously from memory. + +## Enable Feature Flags + +Create the extension, keep a reference to it, and add it to `extensions`. + +```ts title="flags.ts" +import { init } from "@faststats/web"; +import { featureFlags } from "@faststats/web/flags"; + +export const flags = featureFlags({ + flags: ["new_checkout", "theme", "max_items"], +}); + +init({ + siteKey: "your_site_key", + extensions: [flags], +}); +``` + +Flags load as soon as tracking starts. They load again when the visitor is +identified or logs out, and when consent switches between `"anonymous"` and +`"granted"`. With consent `"denied"`, no flags are loaded and every flag returns +its default. + +## Options + +| Option | Type | Default | Description | +| ------------ | ------------------------- | ----------------------------- | ------------------------------------------------- | +| `flags` | `string[]` | all flags | Flag keys this app reads | +| `userId` | `string` | none | Your own ID for a signed-in user | +| `attributes` | `Record` | `{}` | Values that targeting rules can match on | +| `endpoint` | `string` | `https://flags.faststats.dev` | Base URL of the feature flags service | +| `timeoutMs` | `number` | `3000` | Aborts a slow load so flags serve their defaults | +| `fetch` | `typeof fetch` | global `fetch` | Custom fetch implementation, for example in tests | + +Every loaded flag is recorded as an exposure. List the keys your app reads in +`flags` so flags the app never shows stay out of your analytics. + +## Read Flags + +`get` returns the flag's value, or `defaultValue` while flags are loading, when +the flag does not exist or is archived, or when its type differs from the +default's type. + +```ts +import { flags } from "./flags"; + +if (flags.isEnabled("new_checkout")) { + // Show the new checkout. +} + +const theme = flags.get("theme", "light"); +const maxItems = flags.get("max_items", 10); +``` + +`isEnabled(key)` is the same as `get(key, false)`. Use `details(key)` to see +why a flag has its value. It returns `undefined` for a flag that is not loaded. + +```ts +const result = flags.details("new_checkout"); +// { value: true, variant: "on", reason: "rule_match" } +``` + +| Reason | Meaning | +| --------------------- | ---------------------------------------------- | +| `disabled` | The flag is turned off | +| `prerequisite_failed` | A prerequisite flag did not match | +| `target_match` | The visitor is targeted directly | +| `rule_match` | A targeting rule matched | +| `fallthrough` | No target or rule matched, so the default rule | + +## Wait for Flags + +Flags start out empty, so reads return their defaults until the first load +finishes. Wait for `ready()` when a value must be correct before you render. + +```ts +await flags.ready(); +renderCheckout(flags.isEnabled("new_checkout")); +``` + +`ready()` never rejects. Check `status` to tell a successful load from a failed +one. + +| Status | Meaning | +| --------- | -------------------------------------------------- | +| `idle` | The client has not started tracking yet | +| `loading` | The first load for the current visitor | +| `ready` | Flags are loaded | +| `error` | The load failed, so every flag returns its default | + +A failed load is logged when `debug` is on. Call `reload()` to try again. +`reload()` never rejects either. + +## React to Changes + +`subscribe` calls a listener whenever the flags or `status` change, and returns a +function that removes it. + +```ts +const unsubscribe = flags.subscribe(() => { + updateCheckout(flags.isEnabled("new_checkout")); +}); +``` + +## Users and Attributes + +Rollouts follow the visitor's anonymous identifier. Once you call +[`identify`](/web-analytics/identify), flags reload with the external ID as +`userId`, so rollouts follow a signed-in user across devices. `logout` clears it +again. + +Use `setContext` to change the user or attributes yourself. It loads the flags +again and returns a promise that resolves when the load settles. + +```ts +await flags.setContext({ + userId: "user_123", + attributes: { plan: "pro", country: "DE" }, +}); +``` + +`attributes` replaces the attributes set before. Pass `userId: null` to go back +to the anonymous visitor. diff --git a/content/docs/web-analytics/meta.json b/content/docs/web-analytics/meta.json index b9d8e8f..27b8929 100644 --- a/content/docs/web-analytics/meta.json +++ b/content/docs/web-analytics/meta.json @@ -17,6 +17,7 @@ "error-tracking", "sourcemaps", "web-vitals", - "session-replay" + "session-replay", + "feature-flags" ] }