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 on the web client and server, sync targeting context, and add new flag keys to TurboStarter.

Once a provider is active, reading a flag is a one-liner. Use React hooks in Client Components and the @workspace/flags-web/server helpers in Server Components, route handlers, and other server code.

Flag keys

Keys live in one place so every platform stays aligned:

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

Import Flag from @workspace/flags and pass the constant (not a raw string) into evaluators. That keeps typos out of production and makes refactors easy.

Client-side evaluation

OpenFeature hooks are re-exported from @workspace/flags:

"use client";

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

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

  if (!enabled) {
    return null;
  }

  return <button type="button">Try the new checkout</button>;
};

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.

Server-side evaluation

Web is the only platform package that exposes a server API. Prefer it in RSC pages so gated UI never flashes the wrong state:

apps/web/src/app/[locale]/dashboard/(user)/settings/page.tsx
import { Flag } from "@workspace/flags";
import { getBooleanValue } from "@workspace/flags-web/server";

const demo = await getBooleanValue(Flag.DEMO, false, {
  targetingKey: user.id,
});

Available helpers from @workspace/flags-web/server:

  • getBooleanValue(flag, defaultValue, context?)
  • getStringValue(flag, defaultValue, context?)
  • getNumberValue(flag, defaultValue, context?)
  • getObjectValue(flag, defaultValue, context?)

Pass targetingKey (and optional traits) when the provider needs identity for rules. The demo settings page does exactly that with the signed-in user id.

Targeting context

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

apps/web/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. When you do (for example, org-scoped targeting), import them from @workspace/flags-web.

With PostHog, syncContext maps this to identify / reset so dashboard rules see the same person as your analytics events.

Add a new flag

Declare the key

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

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

Update in-memory defaults

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

[Flag.NEW_CHECKOUT]: {
  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_CHECKOUT). Configure rollouts and targeting there.

Evaluate it in the UI

Use a hook on the client or getBooleanValue on the server:

const showCheckout = useBooleanFlagValue(Flag.NEW_CHECKOUT, false);

Troubleshooting

SymptomWhat to check
Demo banner never appearsIn-memory defaultVariant is "on" by default. If you switched to PostHog/GrowthBook, ensure a remote flag named demo exists and is enabled for your user.
Client and server disagreeConfirm providers/index.ts, server.ts, and env.ts all point at the same provider.
Remote rules ignore the userConfirm you are signed in so targetingKey is set. Check PostHog person / GrowthBook attributes.
Missing env / boot errorsSet NEXT_PUBLIC_POSTHOG_* or NEXT_PUBLIC_GROWTHBOOK_* for the active provider. Restart pnpm dev after changes.

How is this guide?

Last updated on

On this page

Ship your startup everywhere. In minutes.Try TurboStarter