Install guides / Angular

Bug reports from your Angular app

The widget is a script tag, and in an Angular app the place for it is src/index.html, the page the app boots in. The router only re-renders inside <app-root>, so the widget stays put.

The tag, in src/index.html

src/index.html
<body>
  <app-root></app-root>
  <script
    src="https://app.brainbox.sh/widget.js"
    data-project="pk_your_project_key"
    data-endpoint="https://app.brainbox.sh/ingest"
  ></script>
</body>

Not in angular.json's scripts: that option bundles files from your project into a script of its own, and can't load a URL or carry the data attributes the widget reads its settings from.

Content Security Policy

Angular's security.autoCsp option rewrites the page's <script src> tags into a hashed loader that keeps only a few standard attributes, so data-project and data-endpoint don't survive and the widget can't find its settings. Leave autoCsp off on pages with the widget, or set your policy yourself and add https://app.brainbox.sh to script-src and connect-src.

Who is signed in

Wherever your app holds the current user, pass it on when it changes. With a signal, an effect does it:

src/app/feedback.service.ts
import { Injectable, effect, inject } from "@angular/core"
import { AuthService } from "./auth.service" // yours

@Injectable({ providedIn: "root" })
export class FeedbackService {
  private auth = inject(AuthService)

  constructor() {
    effect(() => {
      const user = this.auth.user()
      if (user) window.Brainbox?.identify({ id: user.id, email: user.email, name: user.name })
    })
  }

  reportBug() {
    window.Brainbox?.start("screenshot")
  }
}

Inject the service once, in your root component, so it starts with the app.

TypeScript

window.Brainbox comes from a script tag, so TypeScript doesn't know it. Declare it once:

src/app/brainbox.d.ts
declare global {
  interface Window {
    Brainbox?: {
      identify(user: { id?: string; email?: string; name?: string }): void
      open(): void
      start(mode: "screenshot" | "record" | "feature"): void
      close(): void
    }
  }
}

export {}

Your own button

Any element marked data-brainbox opens the widget, in any component's template. screenshot goes straight to marking up the page, record to a recording of the page and feature to a feature request; an empty value opens the chooser. Clicks are delegated, so elements rendered later work too.

<button data-brainbox="screenshot">Report a bug</button>
<button data-brainbox="feature">Request a feature</button>

From code, window.Brainbox.open() shows the chooser and window.Brainbox.start("feature") goes straight to one flow. To hide our floating button and keep only yours, add data-mode="mount" to the tag.

Passwords and personal data

Typed values are always masked in recordings, so passwords, emails and card numbers never leave the browser as plain text. Add the rr-block or rr-mask class to anything else you want kept out.

Try it before you install it

The button in the corner of this page is the real widget. Report something on this page and it lands in our inbox, the way your users’ reports will land in yours.

Get started