Skip to main content

Angular SDK

Use Toggly's Angular SDK in Angular applications.

Installation​

Install the Angular feature flags package using NPM:

$ npm i -s @ops-ai/ngx-feature-flags-toggly

Standalone application setup (Angular 22)​

For a standalone Angular application, register Toggly with provideToggly and import NgxFeatureFlagsTogglyModule in the component that uses its templates. Use the following browser build and change-detection settings so both the native <feature> component and *featureFlag directive update after asynchronous evaluation.

Browser build configuration​

The signature verifier uses native WebCrypto in the browser and Node's crypto module on the server. Angular's application builder can encounter the Node import while bundling. Add externalDependencies to your existing project's architect.build.options in angular.json, preserving its other options:

{
"externalDependencies": ["crypto"]
}

This excludes the guarded Node-only dependency from the browser bundle; it does not provide a crypto polyfill or disable signature verification. Serve the application over HTTPS or localhost, where browser WebCrypto is available. Do not add a browser shim that makes process.versions.node appear to be a Node runtime. See Angular's build options.

Bootstrap and change detection​

Install Zone.js alongside the SDK:

npm install @ops-ai/ngx-feature-flags-toggly zone.js

Enable zone-based change detection and use an eager host component. Angular 22's default zoneless/OnPush setup does not automatically observe the native Feature component's plain fields when its asynchronous flag check completes. Adding Zone.js alone while leaving the host OnPush is insufficient for this integration. See provideZoneChangeDetection and ChangeDetectionStrategy.

In src/app/app.ts:

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { NgxFeatureFlagsTogglyModule } from '@ops-ai/ngx-feature-flags-toggly';

@Component({
selector: 'app-root',
changeDetection: ChangeDetectionStrategy.Eager,
imports: [NgxFeatureFlagsTogglyModule],
template: `
<feature featureKey="new-dashboard">
<ng-template featureTemplate>New dashboard</ng-template>
</feature>
<p *featureFlag="'new-dashboard'">Dashboard actions are available.</p>
`,
})
export class App {}

In src/app/app.config.ts, supply your public Toggly App Key and environment. If user identity, groups or claims are already known at startup, include them in this same configuration.

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideToggly } from '@ops-ai/ngx-feature-flags-toggly';

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection(),
provideToggly({
appKey: 'your-app-key',
environment: 'Production',
verifySignatures: true,
featureDefaults: { 'new-dashboard': false },
}),
],
};

In src/main.ts, load Zone.js before bootstrapping:

import 'zone.js';
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app/app';
import { appConfig } from './app/app.config';

bootstrapApplication(App, appConfig).catch(console.error);

Keep your existing router and other providers when applying this setup. Run ng build to verify the production application bundle. The module-based setup below remains available for applications using NgModules.

Basic Usage (with Toggly.io)​

Setup Module​

Import the Toggly library in your main module file:

import { NgxFeatureFlagsTogglyModule } from '@ops-ai/ngx-feature-flags-toggly'

Import the Toggly library while providing your App Key & Environment name from your Toggly application page:

@NgModule({
declarations: [AppComponent],
imports: [
...
NgxFeatureFlagsTogglyModule.forRoot({
appKey: 'your-app-key', // You can find this in Toggly.io
environment: 'your-environment-name', // You can find this in Toggly.io
onError: (message, error) => {
// Report fetch/cache/refresh failures to your monitoring provider
console.warn('Toggly error:', message, error);
},
}),
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

Users and Rollouts​

Using this package with Toggly allows you to define custom feature rollouts.

Custom rollouts offers the ability to show features only to certain groups of users based on various custom rules which you can define in Toggly.

In case you want to support custom feature rollouts, remember to provide an unique identity string for each user to make sure they get the same feature values on future visits:

NgxFeatureFlagsTogglyModule.forRoot({
appKey: 'your-app-key', // You can find this in Toggly.io
environment: 'your-environment-name', // You can find this in Toggly.io
identity: 'unique-user-identifier', // Stable user identifier for rollouts
groups: ['beta'],
claims: { role: 'admin' },
maxCacheKeys: 20, // Optional LRU cap for identity-scoped cache keys
}),

See Client-side cache limits for maxCacheKeys behavior.

Supply known identity, groups, and claims in your initial TogglyOptions configuration so the first request has the complete targeting context. For a later login or membership change, inject TogglyService and call setContext(); it refreshes the evaluated flags:

await this.togglyService.setContext({
identity: user.id,
groups: user.groups,
claims: { role: user.role, plan: user.plan },
});

See Feature filters and Evaluated-signed.

success

When using user identifiers, evaluated features are cached per user for 30 minutes by default on a sliding window, so the user might not see the change right away as to not confuse the user. The session length is configurable and the session store can be cleared in App Settings

Feature Flag Directive​

The *featureFlag directive allows you to conditionally show/hide elements in your templates:

<div *featureFlag="'firstFeature'">
<p>This feature can be turned on or off.</p>
</div>

Directive Options​

You can also check multiple feature keys and make use of the requirement (all/any) and negate (bool) parameters (requirement is set to "all" by default).

Show if all features are on​

<div *featureFlag="['firstFeature', 'secondFeature']">
<p>ALL the provided feature keys are TRUE.</p>
</div>

Show if any feature is on​

<div *featureFlag="['firstFeature', 'secondFeature']" featureFlagRequirement="any">
<p>AT LEAST ONE the provided feature keys is TRUE.</p>
</div>

Show if features are off (negate)​

<div *featureFlag="['firstFeature', 'secondFeature']" featureFlagRequirement="all" [featureFlagNegate]="true">
<p>NONE of the provided feature keys is TRUE.</p>
</div>

Feature Component​

The <feature> component provides an alternative way to conditionally render content:

<feature featureKey="firstFeature">
<ng-template featureTemplate>
<p>This feature can be turned on or off.</p>
</ng-template>
</feature>

Component Options​

You can also check multiple feature keys and make use of the requirement (all/any) and negate (bool) options (requirement is set to "all" by default).

Show if all features are on​

<feature [featureKeys]="['firstFeature', 'secondFeature']">
<ng-template featureTemplate>
<p>ALL the provided feature keys are TRUE.</p>
</ng-template>
</feature>

Show if any feature is on​

<feature [featureKeys]="['firstFeature', 'secondFeature']" requirement="any">
<ng-template featureTemplate>
<p>AT LEAST ONE the provided feature keys is TRUE.</p>
</ng-template>
</feature>

Show if features are off (negate)​

<feature [featureKeys]="['firstFeature', 'secondFeature']" requirement="all" [negate]="true">
<ng-template featureTemplate>
<p>NONE of the provided feature keys is TRUE.</p>
</ng-template>
</feature>

Feature vs FeatureGateBuilder​

NeedUse
Remove content from the DOM when off<feature> or *featureFlag
Keep content mounted; drive CSS, disabled state, or handlers*featureGateBuilder

FeatureGateBuilder directive​

Use *featureGateBuilder when you need the resolved gate boolean for styling or behavior while keeping the template mounted:

<button
*featureGateBuilder="'PremiumCheckout'; let enabled"
[class.active]="enabled"
[disabled]="!enabled"
>
Checkout
</button>

Multiple keys and options:

<div
*featureGateBuilder="['feature-a', 'feature-b']; requirement: 'any'; let enabled"
>
<p [class.text-muted]="!enabled">Beta tools</p>
</div>

The <feature> component, *featureFlag, and *featureGateBuilder re-evaluate when remote flags refresh, when featureFlagRequirement / featureGateBuilderRequirement or negate bindings change, and when device-local post-filter gates change via notifyLocalGatesChanged().

Feature Flag Guard​

The FeatureFlagGuard allows you to protect routes based on feature flags:

@NgModule({
imports: [
RouterModule.forRoot([
{
path: 'experimental-route',
loadChildren: () => import('/path/to/module').then((module) => module.ExperimentalModuleName),
canActivate: [FeatureFlagGuard],
data: {
featureFlag: 'firstFeature',
featureFlagRedirect: '/path/for/redirect', // URL Path to redirect in case feature flag should not be displayed
},
}
])
]
})

Guard Options​

You can also check multiple feature keys and make use of the featureFlagRequirement (all/any) and featureFlagNegate (bool) options (requirement is set to "all" by default).

@NgModule({
imports: [
RouterModule.forRoot([
{
path: 'experimental-route',
loadChildren: () => import('/path/to/module').then((module) => module.ExperimentalModuleName),
canActivate: [FeatureFlagGuard],
data: {
featureFlag: 'firstFeature',
featureFlagRequirement: 'any',
featureFlagNegate: true,
featureFlagRedirect: '/path/for/redirect',
},
}
])
]
})

Programmatic Access​

You can use the TogglyService to check if a feature is ON or OFF programmatically, by simply injecting it in any component:

@Component({
selector: 'app-random',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class RandomComponent {
constructor(private toggly: TogglyService) { }
}

Check if a feature is on​

this.toggly
.isFeatureOn('firstFeature')
.then(isEnabled => { /* Checks if feature is enabled */ })

Check if a feature is off​

this.toggly
.isFeatureOff('firstFeature')
.then(isDisabled => { /* Checks if feature is disabled */ })

Evaluate a feature gate​

this.toggly
.evaluateFeatureGate(['firstFeature', 'secondFeature'], 'any', false)
.then(isEnabled => { /* Checks if at least one of the provided features is enabled */ })

Basic Usage (without Toggly.io)​

You can also use the Angular SDK without connecting to Toggly.io by providing feature defaults:

Setup Module with Defaults​

Import the Toggly library in your main file:

import { NgxFeatureFlagsTogglyModule } from '@ops-ai/ngx-feature-flags-toggly'

Import the Toggly library while providing your default feature flags:

@NgModule({
declarations: [AppComponent],
imports: [
...
NgxFeatureFlagsTogglyModule.forRoot({
featureDefaults: {
firstFeature: true,
secondFeature: false,
}
}),
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

Now you can use the Feature Flag directive, Feature component & Feature Flag Guard the same way as with Toggly.io.

Error reporting and refresh behavior​

Pass onError to NgxFeatureFlagsTogglyModule.forRoot() to observe fetch, cache, parse, and refresh failures:

NgxFeatureFlagsTogglyModule.forRoot({
appKey: 'your-app-key',
environment: 'your-environment-name',
onError: (message, error) => {
monitoring.captureException(error, {
tags: { source: 'toggly' },
extra: { message }
});
},
});

TogglyService.lastError is updated before onError runs. After one successful load, refresh failures preserve the last-known-good flags instead of replacing rendered content with defaults or empty flags. The *featureFlag directive, featureVariant directive, and <feature> component subscribe to flag refreshes, so interval and WebSocket updates can refresh Angular views automatically.

For the shared reliability contract, see Reliability and Error Handling.

Best Practices​

  1. Import Module Once: Import NgxFeatureFlagsTogglyModule in your root module
  2. Use Directives: Prefer using the *featureFlag directive for simple conditional rendering
  3. Use Components: Use the <feature> component for more complex templates
  4. Use Guards: Protect routes with FeatureFlagGuard for route-level feature control
  5. Inject Service: Use TogglyService for programmatic feature checks
  6. Provide User Context: Include identity for accurate targeting and rollouts
  7. Set Feature Defaults: Provide defaults for offline scenarios or when not using Toggly.io

Extensibility with Hooks​

Toggly provides a powerful hooks system that allows you to extend SDK functionality by hooking into feature flag lifecycle events. This is perfect for integrating with analytics platforms like Microsoft Clarity, monitoring tools, or implementing custom behaviors.

What are Hooks?​

Hooks let you execute custom code at specific points in the feature flag evaluation lifecycle:

  • beforeEvaluation: Called before a feature flag is evaluated
  • afterEvaluation: Called after a feature flag is evaluated (with the result)
  • beforeIdentify: Called before user identity is set or cleared
  • afterIdentify: Called after user identity is set or cleared
  • afterRefresh: Called after feature definitions are refreshed from Toggly

Creating a Hook​

import { Hook } from '@ops-ai/toggly-hooks-types';

export const myAnalyticsHook: Hook = {
getMetadata: () => ({
name: 'MyAnalyticsHook',
version: '1.0.0'
}),

afterEvaluation: async (flagKey, _data, result) => {
// Send to analytics
console.log('Feature Flag Evaluated', {
feature: flagKey,
enabled: result
});
}
};

Registering Hooks​

1. During module configuration:​

import { NgModule } from '@angular/core';
import { NgxFeatureFlagsTogglyModule } from '@ops-ai/ngx-feature-flags-toggly';
import { myAnalyticsHook } from './hooks/analytics.hook';

@NgModule({
imports: [
NgxFeatureFlagsTogglyModule.forRoot({
appKey: 'your-app-key',
environment: 'your-environment-name',
hooks: [myAnalyticsHook]
})
]
})
export class AppModule { }

2. At runtime using the service:​

import { Component, OnInit, OnDestroy } from '@angular/core';
import { TogglyService } from '@ops-ai/ngx-feature-flags-toggly';
import { Hook } from '@ops-ai/toggly-hooks-types';

@Component({
selector: 'app-my-component',
template: '...'
})
export class MyComponent implements OnInit, OnDestroy {
private analyticsHook: Hook;

constructor(private togglyService: TogglyService) {
this.analyticsHook = {
getMetadata: () => ({ name: 'Analytics', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
// Your analytics logic
}
};
}

ngOnInit(): void {
this.togglyService.addHook(this.analyticsHook);
}

ngOnDestroy(): void {
this.togglyService.removeHook(this.analyticsHook.getMetadata().name);
}
}

Common Use Cases​

Microsoft Clarity Integration​

import { Hook } from '@ops-ai/toggly-hooks-types';

export const clarityHook: Hook = {
getMetadata: () => ({ name: 'Microsoft Clarity', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
if (typeof (window as any).clarity !== 'undefined') {
(window as any).clarity('event', `FeatureFlag:${flagKey}`);
}
}
};

Angular Service Integration​

import { Injectable } from '@angular/core';
import { TogglyService } from '@ops-ai/ngx-feature-flags-toggly';
import { Hook } from '@ops-ai/toggly-hooks-types';
import { AnalyticsService } from './analytics.service';

@Injectable({ providedIn: 'root' })
export class FeatureFlagAnalyticsService {
private hook: Hook;

constructor(
private togglyService: TogglyService,
private analytics: AnalyticsService
) {
this.hook = {
getMetadata: () => ({ name: 'AnalyticsHook', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
this.analytics.trackFeatureFlag(flagKey, result);
}
};
}

enable(): void {
this.togglyService.addHook(this.hook);
}

disable(): void {
this.togglyService.removeHook(this.hook.getMetadata().name);
}
}

Entity context​

Evaluate flags against a specific domain object on the page. User identity stays on setContext(); entity attributes are per check. See Entity & page context.

Register a mapper on the native service before evaluating an entity:

import { Component, inject } from '@angular/core'
import { TogglyService } from '@ops-ai/ngx-feature-flags-toggly'

type Order = { orderNumber: string; total: number; shipToCountry: string }

@Component({ selector: 'app-orders', templateUrl: './orders.component.html' })
export class OrdersComponent {
private readonly toggly = inject(TogglyService)

constructor() {
this.toggly.registerContext<Order>('Order', (order) => ({
kind: 'Order',
key: order.orderNumber,
attributes: { Total: order.total, Country: order.shipToCountry },
}))
}
}

Structural directive​

<tr *ngFor="let order of orders">
<td>{{ order.orderNumber }}</td>
<td *featureFlag="'ExpressCheckout'; context: order; kind: 'Order'">
Express available
</td>
</tr>

TogglyService​

const showExpress = await this.toggly.isFeatureOn('ExpressCheckout', order, 'Order')

Without context, features that return an EntityGate from evaluated-signed fail closed.

Sample references​

Browse the Toggly Samples catalog for hands-on examples. Start with a sample’s README for setup instructions, then follow its source walkthrough to see how configuration, flag checks, and UI behavior fit together.

Try the Angular showcase and its README. Start with setup and the first-flag exercise, then follow the source walkthrough from native Angular gates to programmatic checks and Order context.

Next Steps​