Angular
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.
Install#
npm i @scalebun/web @scalebun/web-angularPeers: @angular/core >=16, @angular/router >=16, rxjs >=7.
Initialize#
One provider does everything.
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:
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:
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#
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#
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#
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#
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.
async ngOnInit(): Promise<void> { await ScaleBun.whenReady(); ScaleBun.inbox().mount({ trigger: this.el.nativeElement });}Next#
Flags, config & experiments — targeting and rollouts.
Engagement — surveys, in-app messages and push.