ScaleBun
Skip to article

Angular

webDeveloper

Wire ScaleBun into a standalone Angular app with one provider — automatic route tracking, a global ErrorHandler, an injectable service, and coachmark and in-app directives.

Updated Reviewed

Install#

Terminal
npm i @scalebun/web @scalebun/web-angular

Peers: @angular/core >=16, @angular/router >=16, rxjs >=7.

Initialize#

One provider does everything.

src/app/app.config.tsTypeScript
import { provideRouter } from '@angular/router';import { provideScaleBun } from '@scalebun/web-angular';
export const appConfig = {  providers: [    provideRouter(routes),    provideScaleBun({      clientKey: 'skb_live_ck_…',      apiBaseUrl: 'https://api.scalebun.com/api/v1',    }),  ],};

On an NgModule app, use importProvidersFrom or add the same providers to your root module.

provideScaleBun() registers an APP_INITIALIZER that initializes the SDK on the client, subscribes to router navigation, and installs a global ErrorHandler.

Errors#

ScaleBunErrorHandler is installed as Angular's global ErrorHandler, so runtime errors anywhere in the app are captured and then re-logged to the console — Angular's normal behaviour is preserved.

Angular permits exactly one ErrorHandler, so a provider that claims it displaces yours. Opt out and keep your own:

TypeScript
provideScaleBun(config, { errorHandler: false })

provideScaleBun takes a second options argument — errorHandler and trackRoutes, both defaulting to true.

If you would rather keep both behaviours, call into the SDK from your own handler:

TypeScript
import { ErrorHandler, Injectable } from '@angular/core';import ScaleBun from '@scalebun/web-angular';
@Injectable()export class AppErrorHandler implements ErrorHandler {  handleError(error: unknown): void {    ScaleBun.captureError(error instanceof Error ? error : new Error(String(error)));    // …your own reporting  }}

…and provide it after provideScaleBun().

The service#

TypeScript
import { Component, inject } from '@angular/core';import { ScaleBunService } from '@scalebun/web-angular';
@Component({ /* … */ })export class CheckoutComponent {  private readonly scalebun = inject(ScaleBunService);
  buy(): void {    this.scalebun.sdk.track('purchase_started', { plan: 'pro' });  }}

ScaleBunService exposes sdk (the full facade) plus trackScreen, captureError and coachmarks shortcuts.

Feature flags#

TypeScript
import { DestroyRef, inject, signal } from '@angular/core';import ScaleBun from '@scalebun/web-angular';
const cta = signal(ScaleBun.flags.variation('checkout-cta', 'control'));const stop = ScaleBun.flags.onChange(() =>  cta.set(ScaleBun.flags.variation('checkout-cta', 'control')),);inject(DestroyRef).onDestroy(stop);

Coachmarks#

TypeScript
import { ScaleBunAnchorDirective } from '@scalebun/web-angular';
@Component({  imports: [ScaleBunAnchorDirective],  template: `<button scalebunAnchor="checkout">Checkout</button>`,})export class CheckoutComponent {}

Both directives are standalone — add them to a component's imports.

In-app messages#

TypeScript
import { InAppService, InAppSlotDirective } from '@scalebun/web-angular';
@Component({  imports: [InAppSlotDirective],  template: `<div scalebunInAppSlot="empty-state"></div>`,})export class DashboardComponent {}

Waiting for the SDK#

APP_INITIALIZER runs before the first component, so most code can call the facade directly. Component code that reaches for a feature controller should still await whenReady() — it resolves once init() has settled, and an accessor called earlier returns a no-op object.

TypeScript
async ngOnInit(): Promise<void> {  await ScaleBun.whenReady();  ScaleBun.inbox().mount({ trigger: this.el.nativeElement });}

Next#

Angular · Integrations · Web SDK · ScaleBun