For the complete documentation index, see llms.txt. Prefer markdown by appending .md to documentation URLs or sending Accept: text/markdown.

Usage

Evaluate feature flags in extension options and popup UI, sync targeting context from auth, and add new flag keys.

Once a provider is active, reading a flag is a one-liner with OpenFeature hooks from @workspace/flags.

Flag keys

Keys live in one place so web, mobile, and extension stay aligned:

packages/flags/shared/src/keys.ts
export const Flag = {
  DEMO: "demo",
} as const;

Import Flag from @workspace/flags and pass the constant into hooks.

Evaluate in the UI

import { Flag, useBooleanFlagValue } from "@workspace/flags";

export const BetaNotice = () => {
  const enabled = useBooleanFlagValue(Flag.DEMO, false);

  if (!enabled) {
    return null;
  }

  return <p>Beta features unlocked</p>;
};

The options page uses the same pattern for the demo banner:

apps/extension/src/app/options/main.tsx
const DemoBanner = () => {
  const demo = useBooleanFlagValue(Flag.DEMO, false);

  if (!demo) {
    return null;
  }

  // …
};

Other value types:

HookTypical use
useBooleanFlagValueOn/off gates
useStringFlagValueVariant copy, theme names, URLs
useNumberFlagValueLimits, percentages, experiment arms
useObjectFlagValueStructured payloads / config blobs

Always pass a sensible default as the second argument. That value is used while the provider loads, or if evaluation fails.

Targeting context

apps/extension/src/lib/providers/flags.tsx already syncs auth state into OpenFeature:

apps/extension/src/lib/providers/flags.tsx
if (session.data?.user) {
  const { id, email, name } = session.data.user;
  void setContext({ targetingKey: id, email, name });
  return;
}

void clearContext();

You rarely need to call setContext / clearContext yourself. Import them from @workspace/flags-extension when you do.

With PostHog, context sync maps to identify / reset so person-based rules match your signed-in user.

Add a new flag

Declare the key

Add a constant in packages/flags/shared/src/keys.ts:

export const Flag = {
  DEMO: "demo",
  NEW_SIDE_PANEL: "NEW_SIDE_PANEL", 
} as const;

Update in-memory defaults

Give local development a known value in packages/flags/shared/src/in-memory.ts:

[Flag.NEW_SIDE_PANEL]: {
  disabled: false,
  variants: { on: true, off: false },
  defaultVariant: "off",
},

Create it remotely (if needed)

In PostHog or GrowthBook, create a flag with the same key (NEW_SIDE_PANEL). Configure rollouts and targeting there.

Evaluate it in the UI

const showSidePanel = useBooleanFlagValue(Flag.NEW_SIDE_PANEL, false);

Troubleshooting

SymptomWhat to check
Demo banner never appearsIn-memory defaultVariant is "on" by default. For PostHog/GrowthBook, ensure a remote flag named demo exists and targets your user.
Flag stuck on the defaultConfirm the provider export in packages/flags/extension/src/providers/index.ts matches the env vars you set. Restart the Vite extension dev server after env changes.
Remote rules ignore the userSign in so targetingKey is set. Check PostHog person / GrowthBook attributes.
Wrong env prefixExtension uses VITE_POSTHOG_* / VITE_GROWTHBOOK_*, not NEXT_PUBLIC_ or EXPO_PUBLIC_.

How is this guide?

Last updated on

On this page

Ship your startup everywhere. In minutes.Try TurboStarter