---
title: Browser Rules-Based Evaluation
description: >-
  Use DatadogCoreProvider for advanced browser setups with application-managed
  configuration and optional tracking hooks.
breadcrumbs: >-
  Docs > Feature Flags > Implementation Patterns > Browser Rules-Based
  Evaluation
---

> For the complete documentation index, see [llms.txt](https://docs.datadoghq.com/llms.txt).

# Browser Rules-Based Evaluation

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com, us2.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md). ({% placeholder "user-datadog-site-name" /%}).
{% /alert %}

{% /callout %}

## Overview{% #overview %}

**Use `DatadogProvider` for most browser applications.** It manages configuration fetching, context updates, and built-in telemetry. Follow [JavaScript Feature Flags](https://docs.datadoghq.com/feature_flags/client/javascript.md) for that setup.

`DatadogCoreProvider` is an advanced, OpenFeature-compatible provider that evaluates configurations supplied by your application. It does not fetch or poll configuration, install tracking hooks, or send telemetry. With rules-based configuration, it evaluates targeting rules locally when the OpenFeature context changes, without fetching new assignments.

## When to use this approach{% #when-to-use-this-approach %}

Choose `DatadogCoreProvider` when your application needs one or more of these capabilities:

- Evaluate targeting rules for changing contexts without a configuration request for each context change.
- Supply configuration through an application-controlled delivery path, such as a server-rendered bootstrap payload.
- Control configuration refreshes and compose only the tracking integrations the application needs.

Your application owns configuration availability, freshness, and the tracking life cycle.

## Prerequisites{% #prerequisites %}

- Install `@datadog/openfeature-browser` 2.0.0 or later, `@openfeature/web-sdk`, and `@openfeature/core`, following the [JavaScript installation instructions](https://docs.datadoghq.com/feature_flags/client/javascript.md#installation). The examples use npm package imports.
- To fetch rules from Datadog, provide a [client token](https://docs.datadoghq.com/account_management/api-app-keys.md#client-tokens), environment name, and Datadog site.
- Enabling client rules delivery for your organization requires **Feature Flag Environment Config Write** permission. This permission is not needed to use the SDK if the setting is already enabled. See [Permissions and Access Control](https://docs.datadoghq.com/feature_flags/concepts/permissions.md).
- Distribute only browser-appropriate flags to the **Client** [distribution channel](https://docs.datadoghq.com/feature_flags/concepts/distribution_channels.md).

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com, us2.ddog-gov.com



{% alert level="danger" %}
Browser Feature Flags are not supported for the selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md) ({% placeholder "user-datadog-site-name" /%}).
{% /alert %}


{% /callout %}

{% alert level="warning" %}
Configuration delivered to a browser is inspectable, including any targeting rules and values it contains. Keep sensitive configuration and authorization decisions on the server. Client-side feature flags are not an access-control mechanism.
{% /alert %}

## Getting started{% #getting-started %}

For browser applications that change evaluation context repeatedly during a session, use `fetchRulesConfiguration()` with `DatadogCoreProvider`. Load the rules once, then reuse the provider and configuration for subsequent evaluations and context changes.

Feature Flags billing counts [Monthly Flag Configuration Requests (MFCR)](https://docs.datadoghq.com/feature_flags/concepts/monthly_flag_configuration_requests.md), not local evaluations. The initial rules fetch and later refreshes count toward MFCR; local context changes do not.

### Enable client rules delivery{% #enable-client-rules-delivery %}

Before fetching rules, enable client rules delivery for your organization. This setting is disabled by default.

1. Open **Feature Flags** > **Settings** > [**Flag Delivery**](https://app.datadoghq.com/feature-flags/settings/flag-delivery).
1. Enable **Allow local rule evaluation in client SDKs**.
1. Click **Save**.

This setting is separate from the per-flag [**Client** distribution channel](https://docs.datadoghq.com/feature_flags/concepts/distribution_channels.md). The standard precomputed `DatadogProvider` setup does not require it.

### Fetch rules and initialize{% #fetch-rules-and-initialize %}

Import `DatadogCoreProvider` and `fetchRulesConfiguration` from `@datadog/openfeature-browser/rules-based`. Fetch the rules and validate the result before supplying it to the provider:

```javascript
import {
  DatadogCoreProvider,
  fetchRulesConfiguration,
} from '@datadog/openfeature-browser/rules-based';
import { OpenFeature } from '@openfeature/web-sdk';

const configurationOptions = {
  clientToken: '<CLIENT_TOKEN>',
  site: '<YOUR_DATADOG_SITE>',
  env: '<ENV_NAME>',
};

async function loadRulesConfiguration() {
  const configuration = await fetchRulesConfiguration(configurationOptions);
  if (!configuration.rules) {
    throw new Error(configuration.rulesError ?? 'Expected a rules-based configuration');
  }
  return configuration;
}

const configuration = await loadRulesConfiguration();
const provider = new DatadogCoreProvider();
provider.setConfiguration(configuration);

const domain = 'browser-flags';
await OpenFeature.setProviderAndWait(domain, provider, {
  targetingKey: 'user-123',
  plan: 'free',
});

const client = OpenFeature.getClient(domain);
const enabled = client.getBooleanValue('checkout_new', false);
```

The fetch helper makes the configuration request. Registering the provider and evaluating the flag do not fetch configuration. The helper also accepts a `fetch` implementation and an abort `signal` for application-controlled transport.

Catch initialization errors in your application's startup flow and decide whether to retry or continue with default behavior. Do not assume the provider is ready if `setProviderAndWait()` rejects.

### Change the evaluation context{% #change-the-evaluation-context %}

Use the same domain when updating context:

```javascript
await OpenFeature.setContext(domain, {
  targetingKey: 'user-456',
  plan: 'premium',
});

const enabledForUpdatedContext = client.getBooleanValue('checkout_new', false);
```

The rules configuration remains in memory and is evaluated against the new context. Do not call `loadRulesConfiguration()` again for each context change. No configuration request is made. Optional tracking hooks can still send telemetry for evaluations.

Use [flat context attributes](https://docs.datadoghq.com/feature_flags/concepts/evaluation_context.md#context-attributes) and supply the attributes needed for targeting explicitly. `DatadogCoreProvider` does not automatically copy user attributes from RUM.

## Refresh configuration{% #refresh-configuration %}

The provider does not poll for flag changes. Choose when the application refreshes configuration, then replace it before subsequent evaluations:

```javascript
try {
  const nextConfiguration = await loadRulesConfiguration();
  provider.setConfiguration(nextConfiguration);
} catch (error) {
  console.warn('Could not refresh flag configuration; keeping the previous configuration.', error);
}
```

If fetching or parsing fails, the provider continues using the previous configuration. The example logs the error; add retries according to your application's freshness requirements. Changes made in Datadog do not reach this provider until the application supplies updated configuration.

## Optional: supply portable configuration{% #optional-supply-portable-configuration %}

Instead of fetching rules in the browser, your application can deliver a serialized rules configuration. Create this string by calling `configurationToString(configuration)` on a rules configuration that you already fetched. Import this helper from `@datadog/openfeature-browser/rules-based`.

Deliver the resulting string to the browser through your application, for example, in a server-rendered bootstrap payload. Replace `<SERIALIZED_RULES_CONFIGURATION>` below with that string, and use this code instead of the `loadRulesConfiguration()` call in the initialization example:

```javascript
import { configurationFromString } from '@datadog/openfeature-browser/rules-based';

const configuration = configurationFromString('<SERIALIZED_RULES_CONFIGURATION>');
if (!configuration.rules) {
  throw new Error(configuration.rulesError ?? 'Expected a rules-based configuration');
}
```

The serialized string is not the raw binary response from the rules endpoint. Use the framework's safe serialization mechanism when embedding configuration in HTML. Parsing and supplying the string do not make a configuration request.

### Precomputed configurations{% #precomputed-configurations %}

Precomputed configuration contains assignments for a specific context, not rules that can be reevaluated for different contexts. If you supply it to `DatadogCoreProvider`, pass the context returned by `getPrecomputedContext(configuration)` to `setProviderAndWait()`. A mismatched context causes an invalid-context error; the provider does not fetch replacement assignments.

## Add optional tracking hooks{% #add-optional-tracking-hooks %}

The provider performs evaluations without tracking by default. Add only the integrations the application needs:

| Factory                                               | Behavior                                                                                                                      |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `createDatadogExposureLoggingHook(trackingOptions)`   | Deduplicates and batches eligible exposure events.                                                                            |
| `createDatadogEvaluationLoggingHook(trackingOptions)` | Aggregates and sends flag evaluation telemetry.                                                                               |
| `createDatadogRumTrackingHook()`                      | Adds flag evaluations to an existing global `DD_RUM` instance. It does not initialize RUM or populate the evaluation context. |

Use `composeDatadogTrackingHooks()` to initialize and shut down the selected integrations together. Register its hooks on the client from the initialization example before evaluations that need tracking:

```javascript
import {
  composeDatadogTrackingHooks,
  createDatadogExposureLoggingHook,
  createDatadogEvaluationLoggingHook,
  createDatadogRumTrackingHook,
} from '@datadog/openfeature-browser/rules-based';

const trackingOptions = {
  clientToken: '<CLIENT_TOKEN>',
  applicationId: '<APPLICATION_ID>',
  site: '<YOUR_DATADOG_SITE>',
  env: '<ENV_NAME>',
  service: '<SERVICE_NAME>',
};

const tracking = composeDatadogTrackingHooks(
  createDatadogExposureLoggingHook(trackingOptions),
  createDatadogEvaluationLoggingHook(trackingOptions),
  createDatadogRumTrackingHook(),
);

await tracking.initialize();
client.addHooks(...tracking.hooks);

const trackedValue = client.getBooleanValue('checkout_new', false);
```

These hooks run only for evaluations made through this client instance. Continue using the same client for tracked evaluations. Omit an integration's import and factory call to exclude it. RUM tracking requires [Browser RUM](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/browser.md) to be available as `DD_RUM` and can increase RUM-billed event counts.

Exposure and evaluation hooks do not collect events until `initialize()` completes. Exposure events are batched and deduplicated, so a repeated evaluation may not create another exposure request.

When tracking is no longer needed, unregister the hooks and shut down their resources:

```javascript
client.clearHooks();
await tracking.shutdown();
```

`clearHooks()` removes **all client-level hooks** from this client; this example assumes the client is dedicated to this tracking setup. It does not remove global or provider hooks. `shutdown()` requests a final flush and stops tracking timers and subscriptions; returning does not guarantee that every network request has completed. Clearing hooks or removing the core provider alone does not shut down manually created tracking resources.

## Further reading{% #further-reading %}

Additional helpful documentation, links, and articles:

- [JavaScript Feature Flags](https://docs.datadoghq.com/feature_flags/client/javascript.md)
- [Evaluation Context](https://docs.datadoghq.com/feature_flags/concepts/evaluation_context.md)
- [Distribution Channels](https://docs.datadoghq.com/feature_flags/concepts/distribution_channels.md)
