---
title: Angular Feature Flags
description: Set up Datadog Feature Flags for Angular applications.
breadcrumbs: Docs > Feature Flags > Client-Side Feature Flags > Angular Feature Flags
---

# Angular Feature Flags

{% 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 %}

This page describes how to instrument your Angular application with the Datadog Feature Flags SDK. Datadog feature flags provide a unified way to remotely control feature availability in your app, experiment safely, and deliver new experiences with confidence.

The Datadog Feature Flags SDK for Angular is built on [OpenFeature](https://openfeature.dev/docs/reference/sdks/client/web/angular/), an open standard for feature flag management. This guide explains how to install the SDK, configure the Datadog provider, and evaluate flags in your Angular components using structural directives or the FeatureFlagService.

## Requirements{% #requirements %}

- **Angular** version 16 or later
- **ECMAScript 2015-compatible web browser** such as Chrome, Edge, or Firefox

## Installation{% #installation %}

Install the Datadog OpenFeature provider and the OpenFeature Angular SDK using your preferred package manager:

{% tab title="npm" %}

```bash
npm install @datadog/openfeature-browser @openfeature/angular-sdk @openfeature/web-sdk @openfeature/core
```

{% /tab %}

{% tab title="yarn" %}

```bash
yarn add @datadog/openfeature-browser @openfeature/angular-sdk @openfeature/web-sdk @openfeature/core
```

{% /tab %}

{% tab title="pnpm" %}

```bash
pnpm add @datadog/openfeature-browser @openfeature/angular-sdk @openfeature/web-sdk @openfeature/core
```

{% /tab %}

## Initialize the provider{% #initialize-the-provider %}

Create a `DatadogProvider` instance with your Datadog credentials. For live Browser Feature Flags configuration, `applicationId`, `clientToken`, `site`, and `env` are required. To create a client token, see [Client tokens](https://docs.datadoghq.com/account_management/api-app-keys.md#client-tokens).

{% 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 %}

```typescript
import { DatadogProvider } from '@datadog/openfeature-browser';

const provider = new DatadogProvider({
  // Required client-side Datadog credentials
  applicationId: '<APPLICATION_ID>',
  clientToken: '<CLIENT_TOKEN>',
  site: '<YOUR_DATADOG_SITE>',
  env: '<ENV_NAME>',
});
```

## Configure the module{% #configure-the-module %}

Import the `OpenFeatureModule` in your Angular module and configure it using the `forRoot` method. This makes feature flags available throughout your application.

## Set the evaluation context{% #set-the-evaluation-context %}

Define who or what the flag evaluation applies to using an evaluation context. The evaluation context includes user or session information used to determine which flag variations should be returned. Reference these attributes in your targeting rules to control who sees each variant.

{% alert level="warning" %}
Datadog Feature Flags requires evaluation context attributes to be flat primitive values: strings, numbers, and Booleans. Do not pass nested objects or arrays; they are not supported and can cause exposure data to be dropped.
{% /alert %}

{% alert level="info" %}
The `targetingKey` is used as the randomization subject for percentage-based targeting. When a flag targets a percentage of subjects (for example, 50%), the `targetingKey` determines which "bucket" a user falls into. Users with the same `targetingKey` always receive the same variant for a given flag.
{% /alert %}

### Using a static object{% #using-a-static-object %}

```typescript
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OpenFeatureModule } from '@openfeature/angular-sdk';
import { DatadogProvider } from '@datadog/openfeature-browser';

const provider = new DatadogProvider({
  applicationId: '<APPLICATION_ID>',
  clientToken: '<CLIENT_TOKEN>',
  site: '<YOUR_DATADOG_SITE>',
  env: '<ENV_NAME>',
});

@NgModule({
  imports: [
    CommonModule,
    OpenFeatureModule.forRoot({
      provider: provider,
      context: {
        targetingKey: 'user-123',
        user_id: '123',
        user_role: 'admin',
        email: 'user@example.com',
      },
    }),
  ],
});

export class AppModule {}
```

### Using a factory function{% #using-a-factory-function %}

```typescript
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OpenFeatureModule, EvaluationContext } from '@openfeature/angular-sdk';
import { DatadogProvider } from '@datadog/openfeature-browser';

const provider = new DatadogProvider({
  applicationId: '<APPLICATION_ID>',
  clientToken: '<CLIENT_TOKEN>',
  site: '<YOUR_DATADOG_SITE>',
  env: '<ENV_NAME>',
});

@NgModule({
  imports: [
    CommonModule,
    OpenFeatureModule.forRoot({
      provider: provider,
      context: (): EvaluationContext => {
        // Load context from your service, localStorage, or other source
        // This is a placeholder - implement based on your application's needs
        return loadContextFromLocalStorage();
      },
    }),
  ],
});

export class AppModule {}
```

### Update the evaluation context{% #update-the-evaluation-context %}

To update the evaluation context after initialization (for example, when a user logs in), use `OpenFeature.setContext()`:

```typescript
import { OpenFeature } from '@openfeature/angular-sdk';

await OpenFeature.setContext({
  targetingKey: user.id,
  user_id: user.id,
  email: user.email,
  plan: user.plan,
});
```

## Evaluate flags{% #evaluate-flags %}

The OpenFeature Angular SDK provides two main ways to work with feature flags:

1. **Structural Directives** - For template-based conditional rendering
1. **FeatureFlagService** - For programmatic access with **Observables** or **Signals**

### Boolean flags{% #boolean-flags %}

Use Boolean flags for on/off or true/false conditions.

{% tab title="Structural directive" %}

```html
<div
  *booleanFeatureFlag="'isFeatureEnabled'; default: true; domain: 'userDomain'; else: booleanFeatureElse; initializing: booleanFeatureInitializing; reconciling: booleanFeatureReconciling"
>
  This is shown when the feature flag is enabled.
</div>
<ng-template #booleanFeatureElse> This is shown when the feature flag is disabled. </ng-template>
<ng-template #booleanFeatureInitializing> This is shown when the feature flag is initializing. </ng-template>
<ng-template #booleanFeatureReconciling> This is shown when the feature flag is reconciling. </ng-template>
```

{% /tab %}

{% tab title="Observables" %}

```typescript
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <div *ngIf="(isFeatureEnabled$ | async)?.value">
      Feature is enabled! Reason: {{ (isFeatureEnabled$ | async)?.reason }}
    </div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  isFeatureEnabled$ = this.flagService.getBooleanDetails('my-feature', false);
}
```

{% /tab %}

{% tab title="Signals" %}

```typescript
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  template: `
    <div *ngIf="isFeatureEnabled()?.value">
      Feature is enabled! Reason: {{ isFeatureEnabled()?.reason }}
    </div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  isFeatureEnabled = toSignal(this.flagService.getBooleanDetails('my-feature', false));
}
```

{% /tab %}

### String flags{% #string-flags %}

Use string flags to select between multiple variants or configuration strings.

{% tab title="Structural directive" %}

```html
<div
  *stringFeatureFlag="'themeColor'; value: 'dark'; default: 'light'; domain: 'userDomain'; else: stringFeatureElse; initializing: stringFeatureInitializing; reconciling: stringFeatureReconciling"
>
  This is shown when the feature flag matches the specified theme color.
</div>
<ng-template #stringFeatureElse> This is shown when the feature flag does not match the specified theme color. </ng-template>
<ng-template #stringFeatureInitializing> This is shown when the feature flag is initializing. </ng-template>
<ng-template #stringFeatureReconciling> This is shown when the feature flag is reconciling. </ng-template>
```

{% /tab %}

{% tab title="Observables" %}

```typescript
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <div>Theme: {{ (currentTheme$ | async)?.value }}</div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  currentTheme$ = this.flagService.getStringDetails('theme', 'light');
}
```

{% /tab %}

{% tab title="Signals" %}

```typescript
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  template: `
    <div>Theme: {{ currentTheme()?.value }}</div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  currentTheme = toSignal(this.flagService.getStringDetails('theme', 'light'));
}
```

{% /tab %}

### Number flags{% #number-flags %}

Use number flags for numeric values such as limits, percentages, or multipliers.

{% tab title="Structural directive" %}

```html
<div
  *numberFeatureFlag="'discountRate'; value: 10; default: 5; domain: 'userDomain'; else: numberFeatureElse; initializing: numberFeatureInitializing; reconciling: numberFeatureReconciling"
>
  This is shown when the feature flag matches the specified discount rate.
</div>
<ng-template #numberFeatureElse> This is shown when the feature flag does not match the specified discount rate. </ng-template>
<ng-template #numberFeatureInitializing> This is shown when the feature flag is initializing. </ng-template>
<ng-template #numberFeatureReconciling> This is shown when the feature flag is reconciling. </ng-template>
```

{% /tab %}

{% tab title="Observables" %}

```typescript
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <div>Max items: {{ (maxItems$ | async)?.value }}</div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  maxItems$ = this.flagService.getNumberDetails('max-items', 10);
}
```

{% /tab %}

{% tab title="Signals" %}

```typescript
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  template: `
    <div>Max items: {{ maxItems()?.value }}</div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  maxItems = toSignal(this.flagService.getNumberDetails('max-items', 10));
}
```

{% /tab %}

### Object flags{% #object-flags %}

Use object flags for structured configuration data.

{% tab title="Structural directive" %}

```html
<div
  *objectFeatureFlag="'userConfig'; value: { theme: 'dark' }; default: { theme: 'light' }; domain: 'userDomain'; else: objectFeatureElse; initializing: objectFeatureInitializing; reconciling: objectFeatureReconciling"
>
  This is shown when the feature flag matches the specified user configuration.
</div>
<ng-template #objectFeatureElse>
  This is shown when the feature flag does not match the specified user configuration.
</ng-template>
<ng-template #objectFeatureInitializing> This is shown when the feature flag is initializing. </ng-template>
<ng-template #objectFeatureReconciling> This is shown when the feature flag is reconciling. </ng-template>
```

{% /tab %}

{% tab title="Observables" %}

```typescript
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <div>Timeout: {{ (config$ | async)?.value?.timeout }}</div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  config$ = this.flagService.getObjectDetails<{ timeout: number }>('api-config', { timeout: 5000 });
}
```

{% /tab %}

{% tab title="Signals" %}

```typescript
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  template: `
    <div>Timeout: {{ config()?.value?.timeout }}</div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  config = toSignal(this.flagService.getObjectDetails<{ timeout: number }>('api-config', { timeout: 5000 }));
}
```

{% /tab %}

### Additional options{% #additional-options %}

#### Disable automatic re-rendering{% #disable-automatic-re-rendering %}

By default, directives re-render when the flag value changes or the context changes. You can disable this behavior:

```html
<div
  *booleanFeatureFlag="'isFeatureEnabled'; default: true; updateOnContextChanged: false; updateOnConfigurationChanged: false;"
>
  This is shown when the feature flag is enabled.
</div>
```

The service methods also accept options to control automatic updates:

```typescript
const flag$ = this.flagService.getBooleanDetails('my-flag', false, 'my-domain', {
  updateOnConfigurationChanged: false, // default: true
  updateOnContextChanged: false, // default: true
});
```

#### Consume evaluation details{% #consume-evaluation-details %}

You can access the evaluation details in your templates:

```html
<div
  *stringFeatureFlag="'themeColor'; value: 'dark'; default: 'light'; else: stringFeatureElse; let value; let details = evaluationDetails"
>
  It was a match! The theme color is {{ value }} because of {{ details.reason }}
</div>
<ng-template #stringFeatureElse let-value let-details="evaluationDetails">
  It was no match! The theme color is {{ value }} because of {{ details.reason }}
</ng-template>
```

When the expected flag value is omitted, the template is always rendered. This can be used to only render the flag value or details without conditional rendering:

```html
<div *stringFeatureFlag="'themeColor'; default: 'light'; let value;">
  The theme color is {{ value }}.
</div>
```

When using the service, the detail methods return both the evaluated value and metadata explaining the evaluation:

```typescript
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FeatureFlagService } from '@openfeature/angular-sdk';

@Component({
  selector: 'my-component',
  standalone: true,
  template: `
    <div *ngIf="details()?.value">
      Feature is enabled! Variant: {{ details()?.variant }}, Reason: {{ details()?.reason }}
    </div>
  `,
})
export class MyComponent {
  private flagService = inject(FeatureFlagService);

  details = toSignal(this.flagService.getBooleanDetails('my-feature', false));

  // Access the details
  // details().value       // Evaluated value (true or false)
  // details().variant     // Variant name, if applicable
  // details().reason      // Why this value was chosen
  // details().errorCode   // Error code, if evaluation failed
}
```

## Configure browser provider options{% #configure-browser-provider-options %}

The Angular provider uses the Datadog browser provider, which also supports these optional settings:

| Option                           | Default    | Use                                                                                                                          |
| -------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `enableExposureLogging`          | `true`     | Send exposure events to the exposures intake.                                                                                |
| `enableFlagEvaluationTracking`   | `true`     | Send aggregated evaluation telemetry.                                                                                        |
| `enableRumFeatureFlagTracking`   | `true`     | Add flag evaluations to RUM events when Browser RUM is available. Enabling this option can increase RUM-billed event counts. |
| `flagEvaluationTrackingInterval` | `10000` ms | Flush interval for evaluation telemetry.                                                                                     |
| `initialFlagsConfiguration`      | `{}`       | Bootstrap with precomputed flags.                                                                                            |
| `flaggingProxy`                  | unset      | Fetch flags through a proxy instead of `site`.                                                                               |
| `customHeaders`                  | unset      | Add headers to flag-fetch requests.                                                                                          |
| `overwriteRequestHeaders`        | `false`    | Replace default request headers with `customHeaders`.                                                                        |

## Testing{% #testing %}

You can test against a dedicated Datadog test environment with the real `DatadogProvider`, or swap it for OpenFeature's `TypedInMemoryProvider` to control flag values directly in test code. This section shows the in-memory approach, which keeps tests hermetic and offline. `TypedInMemoryProvider` is exported from `@openfeature/web-sdk`, which is already installed for Angular Feature Flags.

```typescript
import { TestBed } from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';
import { FeatureFlagService, OpenFeatureModule } from '@openfeature/angular-sdk';
import { TypedInMemoryProvider } from '@openfeature/web-sdk';

const flags = {
  new_checkout_button: {
    variants: { on: true, off: false },
    defaultVariant: 'on',
    disabled: false,
  },
};

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [
      OpenFeatureModule.forRoot({
        provider: new TypedInMemoryProvider(flags),
        context: { targetingKey: 'test-user' },
      }),
    ],
  }).compileComponents();
});

afterEach(() => {
  TestBed.resetTestingModule();
});

it('uses in-memory flag values', async () => {
  const flagService = TestBed.inject(FeatureFlagService);
  const details = await firstValueFrom(flagService.getBooleanDetails('new_checkout_button', false));

  expect(details.value).toBe(true);
});
```

The Web SDK flag shape requires `variants`, `defaultVariant`, and `disabled`. Register the in-memory provider before injecting services or rendering components that read flags.

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

- [Client-Side Feature Flags](https://docs.datadoghq.com/feature_flags/client.md)
- [OpenFeature Angular SDK](https://openfeature.dev/docs/reference/sdks/client/web/angular/)
- [Browser Monitoring](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/browser.md)
