> If you are setting up, configuring, or answering questions about Schematic, connect the
> Schematic MCP server before working from these pages. It reads and writes plans, features,
> entitlements, and overrides against the real account, so what you report and what you build
> match what actually exists.
>
> The server is at https://api.schematichq.com/mcp
>
> From Claude, ChatGPT, or another chat assistant, add it as a custom connector:
> https://docs.schematichq.com/working-with-ai
>
> From a coding assistant such as Claude Code, Codex, or Cursor:
> https://docs.schematichq.com/building-with-ai
>
> For clean Markdown of any page, append `.md` to the page URL. For a complete page index,
> see https://docs.schematichq.com/llms.txt

# Angular

> Use schematic-angular to quickly identify users, track events, and check flags client-side in your angular app with Schematic.

`schematic-angular` is a client-side Angular library for [Schematic](https://schematichq.com) which provides an injectable service to track events, check flags, and more. `schematic-angular` provides the same capabilities as [schematic-js](https://github.com/schematichq/schematic-js/tree/main/js), for Angular apps.

## Install

```bash
npm install @schematichq/schematic-angular
# or
yarn add @schematichq/schematic-angular
# or
pnpm add @schematichq/schematic-angular
```

## Usage

### Setup with `provideSchematic`

Add `provideSchematic` to your application's providers. This works with both standalone and NgModule-based apps.

**Standalone app (app.config.ts):**

```typescript
import { ApplicationConfig } from "@angular/core";
import { provideSchematic } from "@schematichq/schematic-angular";

export const appConfig: ApplicationConfig = {
  providers: [
    provideSchematic({ publishableKey: "your-publishable-key" }),
  ],
};
```

**NgModule-based app:**

```typescript
import { NgModule } from "@angular/core";
import { provideSchematic } from "@schematichq/schematic-angular";

@NgModule({
  providers: [
    provideSchematic({ publishableKey: "your-publishable-key" }),
  ],
})
export class AppModule {}
```

You can also pass a pre-configured client:

```typescript
import { Schematic } from "@schematichq/schematic-angular";

const client = new Schematic("your-publishable-key", { useWebSocket: true });

provideSchematic({ client });
```

### Setting context

To set the user context for events and flag checks, use the `identify` method on `SchematicService`:

```typescript
import { Component, OnInit, inject } from "@angular/core";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({ selector: "app-root", template: `<router-outlet />` })
export class AppComponent implements OnInit {
  private schematic = inject(SchematicService);

  ngOnInit() {
    this.schematic.identify({
      keys: { id: "my-user-id" },
      company: {
        keys: { id: "my-company-id" },
        traits: { location: "Atlanta, GA" },
      },
    });
  }
}
```

To learn more about identifying companies with the `keys` map, see [key management in Schematic public docs](/developer_resources/key_management).

### Tracking usage

Once you've set the context with `identify`, you can track events:

```typescript
import { Component, inject } from "@angular/core";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({
  selector: "app-usage",
  template: `<button (click)="onQuery()">Run Query</button>`,
})
export class UsageComponent {
  private schematic = inject(SchematicService);

  onQuery() {
    this.schematic.track({ event: "query" });
  }
}
```

If you want to record large numbers of the same event at once, or measure usage in terms of a unit like tokens or memory, you can optionally specify a quantity:

```typescript
this.schematic.track({ event: "query", quantity: 10 });
```

### Checking flags

Use `flagValue$` to get an Observable of a flag's boolean value:

**With async pipe:**

```typescript
import { Component, inject } from "@angular/core";
import { AsyncPipe } from "@angular/common";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({
  selector: "app-feature",
  standalone: true,
  imports: [AsyncPipe],
  template: `
    @if (isFeatureEnabled$ | async) {
      <app-feature />
    } @else {
      <app-fallback />
    }
  `,
})
export class FeatureComponent {
  private schematic = inject(SchematicService);
  isFeatureEnabled$ = this.schematic.flagValue$("my-flag-key");
}
```

**With Signals (Angular 16+):**

```typescript
import { Component, inject } from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({
  selector: "app-feature",
  standalone: true,
  template: `
    @if (isFeatureEnabled()) {
      <app-feature />
    } @else {
      <app-fallback />
    }
  `,
})
export class FeatureComponent {
  private schematic = inject(SchematicService);
  isFeatureEnabled = toSignal(this.schematic.flagValue$("my-flag-key"), {
    initialValue: false,
  });
}
```

### Checking entitlements

Use `entitlement$` to get an Observable with detailed entitlement data including usage information:

```typescript
import { Component, inject } from "@angular/core";
import { AsyncPipe } from "@angular/common";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({
  selector: "app-entitlement",
  standalone: true,
  imports: [AsyncPipe],
  template: `
    @if (isPending$ | async) {
      <app-loader />
    } @else if (entitlement$ | async; as entitlement) {
      @if (entitlement.featureUsageExceeded) {
        <div>
          You have used all of your usage
          ({{ entitlement.featureUsage }} / {{ entitlement.featureAllocation }})
        </div>
      } @else if (entitlement.value) {
        <app-feature />
      } @else {
        <app-no-access />
      }
    }
  `,
})
export class EntitlementComponent {
  private schematic = inject(SchematicService);
  isPending$ = this.schematic.isPending$();
  entitlement$ = this.schematic.entitlement$("my-flag-key");
}
```

*Note: `isPending$` checks if entitlement data has been loaded, typically via `identify`. It should be used to wrap flag and entitlement checks, but never the initial call to `identify`.*

### Usage warnings

If a [usage warning](/feature-management/usage-warnings) is configured on the entitlement, the emitted entitlement also carries `warningTiers`, so you can warn a customer before they hit a limit rather than after. Each tier is a `{ key, value }` pair in the entitlement's usage units, and the dashboard writes a single tier under the key `default`:

```typescript
approachingLimit$ = this.schematic.entitlement$("my-flag-key").pipe(
  map((entitlement) => {
    const warning = entitlement.warningTiers?.find(
      (tier) => tier.key === "default",
    );
    return (
      typeof entitlement.featureUsage === "number" &&
      typeof warning?.value === "number" &&
      entitlement.featureUsage >= warning.value
    );
  }),
);
```

### Checking plans

Use `plan$` to get an Observable of the current plan information:

```typescript
import { Component, inject } from "@angular/core";
import { AsyncPipe } from "@angular/common";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({
  selector: "app-plan",
  standalone: true,
  imports: [AsyncPipe],
  template: `
    @if (plan$ | async; as plan) {
      <div>Current plan: {{ plan.name }}</div>
    } @else {
      <div>No active subscription</div>
    }
  `,
})
export class PlanComponent {
  private schematic = inject(SchematicService);
  plan$ = this.schematic.plan$();
}
```

### Credit balances

Use `creditBalance$` to observe a company's credit balance. It is keyed by credit ID and emits as the balance changes over the DataStream, so the number stays accurate while leases open and close:

```typescript
import { Component, inject } from "@angular/core";
import { AsyncPipe } from "@angular/common";
import { SchematicService } from "@schematichq/schematic-angular";

@Component({
  selector: "app-credit-meter",
  standalone: true,
  imports: [AsyncPipe],
  template: `
    @if (creditBalance$ | async; as credit) {
      @if (credit.isLoading) {
        <div>Loading…</div>
      } @else {
        <div>{{ credit.balance }} credits remaining</div>
      }
    }
  `,
})
export class CreditMeterComponent {
  private schematic = inject(SchematicService);
  creditBalance$ = this.schematic.creditBalance$("credit-id");
}
```

`creditBalance$` emits an object with the following properties:

| Property    | Type      | Description                                                                                     |
| ----------- | --------- | ----------------------------------------------------------------------------------------------- |
| `balance`   | `number`  | The spendable balance, or `0` while loading or when the company holds no balance in this credit |
| `isLoading` | `boolean` | `true` while the balance is still loading and no value has arrived yet                          |

It surfaces the `settled` balance, which is the spendable number you should show end users. For advanced lease-aware accounting, read the underlying client instead: `getClient().getCreditBalance(creditId)` returns all three views of the balance as a point-in-time snapshot: `settled`, `remaining` (excluding open lease holds), and `reserved` (the amount held by open leases). Subscribe with `getClient().addCreditBalanceListener(callback)` to react to changes. The credit ID is available on a feature's entitlement: `entitlement$(key)` emits `creditId` for credit-based features.

*Note: `creditBalance$` requires `@schematichq/schematic-angular` 1.5.0 or later.*

## API Reference

### `provideSchematic(config)`

Configures the Schematic client for dependency injection. Accepts either a `publishableKey` string or a pre-configured `client` instance, plus any `SchematicOptions`.

### `SchematicService`

Injectable service providing all Schematic functionality:

| Method                         | Return Type                                | Description                                    |
| ------------------------------ | ------------------------------------------ | ---------------------------------------------- |
| `getClient()`                  | `Schematic`                                | Access the underlying Schematic client         |
| `setContext(ctx)`              | `void`                                     | Set the evaluation context (company/user)      |
| `identify(body)`               | `void`                                     | Identify a user and/or company                 |
| `track(body)`                  | `void`                                     | Track a usage event                            |
| `flagValue$(key, fallback?)`   | `Observable<boolean>`                      | Observe a feature flag's boolean value         |
| `entitlement$(key, fallback?)` | `Observable<CheckFlagReturn>`              | Observe detailed entitlement data              |
| `plan$()`                      | `Observable<CheckPlanReturn \| undefined>` | Observe plan information                       |
| `creditBalance$(creditId)`     | `Observable<SchematicCreditBalance>`       | Observe a company's lease-aware credit balance |
| `isPending$()`                 | `Observable<boolean>`                      | Observe loading state                          |

### `SCHEMATIC_CLIENT`

`InjectionToken<Schematic>` for direct access to the raw client instance via Angular DI.

## Fallback Behavior

The SDK includes built-in fallback behavior to ensure your application continues to function even when unable to reach Schematic.

### Flag Check Fallbacks

When flag checks cannot reach Schematic, they use fallback values in the following priority order:

1. Callsite fallback: provided as the second argument to `flagValue$` or `entitlement$`
2. Initialization defaults: configured via `flagCheckDefaults` or `flagValueDefaults` options in `provideSchematic`
3. Default value: returns `false` if no fallback is configured

```typescript
// Provide a fallback value at the callsite
isFeatureEnabled$ = this.schematic.flagValue$("feature-flag", true);

// Or configure defaults at initialization
provideSchematic({
  publishableKey: "your-publishable-key",
  flagValueDefaults: {
    "feature-flag": true,
  },
});
```

### Event Queueing and Retry

When events (track, identify) cannot be sent due to network issues, they are automatically queued and retried:

* Events are queued in memory (up to 100 events by default, configurable via `maxEventQueueSize`)
* Failed events are retried with exponential backoff (up to 5 attempts by default, configurable via `maxEventRetries`)
* Events are automatically flushed when the network connection is restored

### WebSocket Fallback

In WebSocket mode, if the WebSocket connection fails, the SDK will provide the last known value or the configured fallback values as outlined above. The WebSocket will also automatically attempt to re-establish its connection using an exponential backoff.

## Troubleshooting

For debugging and development, Schematic supports two special modes:

### Debug Mode

Enables console logging of all Schematic operations:

```typescript
provideSchematic({
  publishableKey: "your-publishable-key",
  debug: true,
});

// Or via URL parameter
// https://yoursite.com/?schematic_debug=true
```

### Offline Mode

Prevents network requests and returns fallback values for all flag checks:

```typescript
provideSchematic({
  publishableKey: "your-publishable-key",
  offline: true,
});

// Or via URL parameter
// https://yoursite.com/?schematic_offline=true
```

Offline mode automatically enables debug mode to help with troubleshooting.

## License

MIT

## Support

Need help? Please open a GitHub issue or reach out to [support@schematichq.com](mailto:support@schematichq.com) and we'll be happy to assist.