---
title: "OpenFeature Web Provider"
description: "ConfigDirector Provider for the OpenFeature Web SDK"
canonical_url: "https://docs.configdirector.com/sdks/openfeature/web"
---
# OpenFeature Web Provider

> ConfigDirector Provider for the OpenFeature Web SDK

<div hidden="true" dataTitleIcon="openfeature-javascript">



</div>

<external-badges package-url="https://www.npmjs.com/package/@configdirector/openfeature-web-provider" registry="npm" repo="js-sdks/tree/main/packages/openfeature-web-provider">



</external-badges>

## Introduction

The OpenFeature Web Provider is intended to be used in combination with the [OpenFeature Web SDK](https://openfeature.dev/docs/reference/sdks/client/web/). All modern web browsers on popular platforms should be supported.

The ConfigDirector OpenFeature Web Provider is tested on the latest versions of Chrome, Firefox, Safari, and Edge.

Telemetry collection within browser SDKs uses Web Workers to aggregate and send telemetry data. If the provider runs on older browsers without Web Workers support, config evaluation will continue to work but no telemetry data will be collected for that session.

## Installation

The provider can be installed from NPM: [https://www.npmjs.com/package/@configdirector/openfeature-web-provider](https://www.npmjs.com/package/@configdirector/openfeature-web-provider)

<code-group>

```bash [npm]
npm install --save @openfeature/web-sdk @configdirector/openfeature-web-provider
```

```bash [yarn]
yarn add @openfeature/web-sdk @openfeature/core @configdirector/openfeature-web-provider
```

```bash [pnpm]Javascript
pnpm add @openfeature/web-sdk @openfeature/core @configdirector/openfeature-web-provider
```

```bash [bun]
bun add @openfeature/web-sdk @openfeature/core @configdirector/openfeature-web-provider
```

</code-group>

## Configure and initialize the client

1. Create an instance of the provider using your client SDK key. You can retrieve a client SDK key for each environment under `SDK keys` in the dashboard's navigation panel.
2. Set the OpenFeature provider, together with the context you already know, so the first connection carries it.
3. Get a client instance from OpenFeature.

```ts
import { OpenFeature } from "@openfeature/web-sdk";
import { ConfigDirectorProvider } from "@configdirector/openfeature-web-provider";

const provider = new ConfigDirectorProvider("YOUR-CLIENT-SDK-KEY");
try {
  await OpenFeature.setProviderAndWait(provider, {
    targetingKey: "12345", // In OpenFeature, the `targetingKey` represents the context's identifier
    name: "Example User",
    traits: {
      region: "North America", // Any arbitrary traits which can be referenced in targeting rules
    },
  });
} catch (error) {
  console.warn("Continuing until ConfigDirector sends config:", error);
}
const client = OpenFeature.getClient();
```

The provider connects with the context it has when it is registered. Pass the context you already know to `setProviderAndWait`, or set it with `OpenFeature.setContext` before registering the provider, so the first connection carries it.

### Additional configuration options

Additional configuration options can be passed into the provider in the optional second argument of the constructor.

For example, the `metadata` can be provided like this:

```ts
import { ConfigDirectorProvider } from "@configdirector/openfeature-web-provider";

const provider = new ConfigDirectorProvider("YOUR-CLIENT-SDK-KEY", {
  metadata: {
    appName: "YOUR-APP-NAME",
    appVersion: "1.0.2",
  },
});
```

The provider accepts the same configuration options as the Javascript SDK client, [refer to the additional configuration options section of the Javascript SDK for a full list](https://docs.configdirector.com/sdks/browser/javascript#additional-configuration-options).

### Values kept on the device

By default, the provider keeps the last values it received from ConfigDirector in the browser's `localStorage`, and serves them on the next launch of the same context until live values arrive. An application opened without a network, after at least one successful connection, resolves flags to the values ConfigDirector last sent instead of the default values passed to the OpenFeature client.

Registering the provider still waits for live values or the connection `timeout`: `setProviderAndWait` rejects with `ProviderNotReadyError` on a timeout as it does without kept values, and the provider then reports the `STALE` status, with a `Stale` event, while it serves the kept values. Flags resolve to the kept values in that status. Once live values arrive, the provider reports `READY` with a `Ready` event and the kept values are replaced. A context change that cannot connect reports an error and then `STALE` again, and keeps serving the current values. Flags resolved from the kept values carry the `CACHED` reason; see [Evaluation details and hooks](#evaluation-details-and-hooks).

The values are kept for the context they were evaluated for, and served on the next launch only when the evaluation context has the same `kind` and `targetingKey`. A context without a `targetingKey` matches on the same site, as long as the site's data has not been cleared, because ConfigDirector derives its identifier from the instance ID the SDK stores; such a context keeps its percentage rollout bucket for as long as that instance ID lasts. Other attributes take no part in the match: live values correct them within a second when there is a network. A context change keeps serving the current values until the new context's values arrive, as it does with live values.

Turn it off, or supply your own store, with the `caching` option, which takes the same object the [JavaScript SDK](https://docs.configdirector.com/sdks/browser/javascript#values-kept-on-the-device) takes: `enabled`, defaulting to `true`, and `provider`, a `CachingProvider` with `read()`, `write(value)`, `remove()` and `sizeLimit()` in bytes, defaulting to `localStorage` under a key derived from the client SDK key with a size limit of 256 KB. The entry is one string whose format belongs to the SDK and may change between SDK versions. An entry above the size limit is removed with one warning, a provider that fails is logged once and never stops the provider, and nothing is kept in `offline` mode. The entry, and the `localStorage` key the provider uses by default, carry a SHA-256 digest of the client SDK key, never the key itself. Browsers compute that digest only on pages served over `https` or from `localhost`, so on a plain `http` page on another host nothing is kept and a `provider` of your own is not called.

```ts
import { ConfigDirectorProvider } from "@configdirector/openfeature-web-provider";

const provider = new ConfigDirectorProvider("YOUR-CLIENT-SDK-KEY", {
  caching: { enabled: false },
});
```

## Retrieve config values

To retrieve config values, use the OpenFeature client:

```ts
const booleanValue = client.getBooleanValue("my-config-key", false);

const stringValue = client.getStringValue("my-string-config-key", "Default");
```

For additional information regarding the OpenFeature client [refer to the OpenFeature Web SDK documentation](https://openfeature.dev/docs/reference/sdks/client/web/).

### Evaluation details and hooks

The detailed getters, such as `getBooleanDetails`, say why a value was returned:

<table>
<thead>
  <tr>
    <th>
      Outcome
    </th>
    
    <th>
      Reason
    </th>
    
    <th>
      Error code
    </th>
    
    <th>
      Variant
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      A value was found
    </td>
    
    <td>
      <code>
        TARGETING_MATCH
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The value ID
    </td>
  </tr>
  
  <tr>
    <td>
      A value was served from the values kept on the device
    </td>
    
    <td>
      <code>
        CACHED
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The value ID
    </td>
  </tr>
  
  <tr>
    <td>
      The config carries no value
    </td>
    
    <td>
      <code>
        DEFAULT
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      The config key is unknown
    </td>
    
    <td>
      <code>
        ERROR
      </code>
    </td>
    
    <td>
      <code>
        FLAG_NOT_FOUND
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      No config values have arrived yet
    </td>
    
    <td>
      <code>
        ERROR
      </code>
    </td>
    
    <td>
      <code>
        PROVIDER_NOT_READY
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      The value cannot be read as the requested type
    </td>
    
    <td>
      <code>
        ERROR
      </code>
    </td>
    
    <td>
      <code>
        TYPE_MISMATCH
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

The variant is ConfigDirector's ID for the value served, a stable hash you can send to analytics in place of the value itself; values served in `offline` mode have none. In the three error cases the getters return the in-code default value you pass, and OpenFeature runs the `error` stage of your hooks, so an error-monitoring hook sees a missing config or a value of another type. `getObjectValue` with an object as its in-code default value reads only a JSON object, and with an array only a JSON array; JSON of the other shape gives `TYPE_MISMATCH`.

Before version 1.10.0 of the provider, the details carried no reason, variant or error code, and error hooks did not run.

## Update the context

The provider connects with the context it has when it is registered, as shown in [Configure and initialize the client](https://docs.configdirector.com/sdks/openfeature/web#configure-and-initialize-the-client). To change the context afterwards, for example when a user signs in, call `OpenFeature.setContext`. Each change opens a new connection with the new context:

```ts
await OpenFeature.setContext({
  targetingKey: "654321",
  name: "Another User",
  traits: {
    region: "Australia",
  },
});
```

To evaluate flags for an entity other than a user, such as a tenant, set the context kind with a top-level `kind` attribute:

```ts
await OpenFeature.setContext({
  targetingKey: "acme",
  kind: "tenant",
  name: "Acme Inc.",
  traits: {
    plan: "enterprise",
  },
});
```

Leaving `kind` out means `user`. A `kind` inside `traits` is an ordinary trait, not the context kind. A context kind is 1 to 64 lowercase letters, digits, dashes, or underscores. A context with an invalid context kind is ignored: the SDK logs an error, and flags still resolve successfully, as if no context had been given. Context kinds are available from version 1.7.0 of the provider.

<note>

In the OpenFeature Web SDK with the ConfigDirector provider, the context's `targetingKey` will map to ConfigDirector's context `id`, and its `kind` to the context kind.

Register the provider with the context you know when the page loads, so the first connection carries it. A context set only afterwards replaces a first connection that had none, and that first connection counts as a separate [monthly active user](https://docs.configdirector.com/getting-started/pricing-and-billing#billing-metrics).

</note>

For additional information regarding the OpenFeature client [refer to the OpenFeature Web SDK documentation](https://openfeature.dev/docs/reference/sdks/client/web/).

## Test your code

Code that reads flags through OpenFeature is tested by swapping the provider, so it never has to change for a test and nothing from ConfigDirector is involved. The OpenFeature Web SDK ships an in-memory provider for that: `TypedInMemoryProvider`, in `@openfeature/web-sdk` 1.8 and newer, which serves the values your test sets. Earlier versions ship the same provider as `InMemoryProvider`, now deprecated.

Each flag is a set of named variants and the `defaultVariant` it resolves to:

```ts [checkout.test.ts]
import { OpenFeature, TypedInMemoryProvider } from "@openfeature/web-sdk";

const provider = new TypedInMemoryProvider({
  "new-checkout": { variants: { on: true, off: false }, defaultVariant: "on" },
  "max-items": { variants: { default: 20 }, defaultVariant: "default" },
});

afterEach(() => OpenFeature.close());

test("shows the new checkout", async () => {
  await OpenFeature.setProviderAndWait(provider);
  const client = OpenFeature.getClient();

  expect(client.getBooleanValue("new-checkout", false)).toBe(true);
  expect(client.getNumberValue("max-items", 0)).toBe(20);

  await provider.putConfiguration({
    "new-checkout": { variants: { on: true, off: false }, defaultVariant: "off" },
  });
  expect(client.getBooleanValue("new-checkout", true)).toBe(false);
});
```

`putConfiguration` replaces every flag and emits `ConfigurationChanged` with the keys it was given, so a component that re-reads its flags on that event follows the change. A flag can also carry a `contextEvaluator`, a function from the evaluation context to the name of the variant to serve, for a test of code that targets specific users. `OpenFeature` is one global, so shut it down after each test with `OpenFeature.close()`, which lets the next test register its own provider.

A React application that reads flags through `@openfeature/react-sdk` has `OpenFeatureTestProvider` from that package as well, a component that serves the values it is given to the hooks beneath it.


## Sitemap

See the full [sitemap](https://docs.configdirector.com/sitemap.md) for all pages.
