OpenFeature (JavaScript)

PostHog provides official OpenFeature providers for JavaScript, so you can evaluate PostHog feature flags through the standard OpenFeature API. There are two packages, matching OpenFeature's split between the server and web (client) SDKs:

Server (Node.js)

Installation

Terminal
npm install @posthog/openfeature-node-provider @openfeature/server-sdk posthog-node

Usage

The server model is asynchronous and multi-user: the distinct ID arrives per evaluation from the context's targetingKey, and resolution returns a promise. Construct and configure a PostHog client, register the provider, then evaluate flags:

TypeScript
import { OpenFeature } from '@openfeature/server-sdk'
import { PostHogServerProvider } from '@posthog/openfeature-node-provider'
import { PostHog } from 'posthog-node'
// personalApiKey enables local evaluation
const posthog = new PostHog('<ph_project_api_key>', { host: 'https://us.i.posthog.com' })
await OpenFeature.setProviderAndWait(new PostHogServerProvider(posthog))
const client = OpenFeature.getClient()
const isEnabled = await client.getBooleanValue('my-flag', false, { targetingKey: 'user_distinct_id' })

You own the PostHog client lifecycle — call posthog.shutdown() when your app exits.

Options

new PostHogServerProvider(client, options?)

  • defaultDistinctId — distinct ID used when the context has no targetingKey. Defaults to unset, in which case an evaluation without a targetingKey returns your default value with error_code = TARGETING_KEY_MISSING.
  • sendFeatureFlagEvents — whether to emit $feature_flag_called events on each evaluation. Defaults to true.

Browser (web)

Installation

Terminal
npm install @posthog/openfeature-web-provider @openfeature/web-sdk posthog-js

Usage

The browser model is single-user and synchronous: posthog-js owns the user identity and keeps flags in memory, so evaluation is synchronous. The provider reconciles the static evaluation context into the SDK whenever it changes (the OpenFeature onContextChange contract), then reloads flags.

TypeScript
import { OpenFeature } from '@openfeature/web-sdk'
import { PostHogWebProvider } from '@posthog/openfeature-web-provider'
import posthog from 'posthog-js'
posthog.init('<ph_project_api_key>', { api_host: 'https://us.i.posthog.com' })
await OpenFeature.setProviderAndWait(new PostHogWebProvider(posthog))
const client = OpenFeature.getClient()
const isEnabled = client.getBooleanValue('my-flag', false)

Manage the user identity through posthog-js as usual (posthog.identify(...)). The web provider never calls identify(), so targetingKey is not used to switch users.

Options

new PostHogWebProvider(client, options?)

  • sendFeatureFlagEvents — whether to emit $feature_flag_called events on each evaluation. Defaults to true.
  • reloadTimeoutMs — the maximum time initialize/onContextChange waits for posthog-js to (re)load flags before becoming ready anyway, so the OpenFeature client can't get stuck in NOT_READY if the SDK never delivers its flags callback. Defaults to 5000.

Evaluation context

The OpenFeature evaluation context maps to PostHog's flag evaluation inputs.

For the server provider:

OpenFeature contextPostHog
targetingKeydistinctId (required unless defaultDistinctId is set)
groups attributegroups
groupProperties attributegroup properties
any other attributeperson properties

For the web provider, posthog-js owns the user identity, so there is no targetingKey mapping:

OpenFeature contextPostHog
groups attributeposthog.group(type, key)
groupProperties attributeposthog.group(type, key, properties)
any other attributeposthog.setPersonPropertiesForFlags(...)

Supported flag types

Both providers resolve flag types through PostHog's getFeatureFlagResult:

OpenFeature methodResolves to
getBooleanValuewhether the flag is enabled
getStringValuethe multivariate variant key
getNumberValuethe variant parsed as a number
getObjectValuethe flag's JSON payload

A non-existent flag returns your default value with error_code = FLAG_NOT_FOUND. Calling a typed getter on an incompatible flag (for example getStringValue on a boolean flag) returns your default value with error_code = TYPE_MISMATCH, per the OpenFeature spec.

Community questions

Was this page useful?

Questions about this page? or post a community question.