BroadcastChannel

Reactive wrapper around the Broadcast Channel API. Communicate between browser tabs/windows with Angular signals.

Loading demo...

Usage

angular-ts
import { Component, effect } from '@angular/core';
import { broadcastChannel } from '@signality/core';

@Component({
  template: `
    <input (input)="channel.post($event.target.value)" />
    <p>Last message: {{ channel.data() }}</p>
  `,
})
export class SyncedInput {
  readonly channel = broadcastChannel<string>('my-channel'); 
  
  constructor() {
    effect(() => {
      console.log('Received:', this.channel.data());
    });
  }
}

Parameters

ParameterTypeDescription
namestringChannel name (must match across tabs)
optionsBroadcastChannelOptionsOptional configuration (see Options below)

Options

The BroadcastChannelOptions extends WithInjector:

OptionTypeDescription
injectorInjectorOptional injector for DI context

Return Value

The broadcastChannel() function returns a BroadcastChannelRef object:

PropertyTypeDescription
dataSignal<T | null>Last received data
errorSignal<MessageEvent | null>Last error that occurred (see MessageEvent)
isClosedSignal<boolean>Whether the channel is closed
post(data: T) => voidSend message to all tabs
close() => voidClose the channel

Examples

Logout across all tabs

angular-ts
import { Injectable, inject, effect } from '@angular/core';
import { Router } from '@angular/router';
import { broadcastChannel } from '@signality/core';

@Injectable({ /* ... */ })
export class AuthService {
  readonly router = inject(Router);
  readonly logoutChannel = broadcastChannel<boolean>('logout');
  
  constructor() {
    effect(() => {
      if (this.logoutChannel.data() === true) {
        this.router.navigate(['/login']);
      }
    });
  }
  
  logout() {
    // Notify all tabs
    this.logoutChannel.post(true); 
    this.router.navigate(['/login']);
  }
}

Sync user state across tabs

angular-ts
import { Component, effect } from '@angular/core';
import { broadcastChannel } from '@signality/core';

interface UserState {
  isLoggedIn: boolean;
  theme: 'light' | 'dark';
}

@Component({
  template: `
    <button (click)="toggleTheme()">Toggle Theme</button>
    <p>Theme: {{ state()?.theme }}</p>
  `,
})
export class SyncedSettings {
  readonly channel = broadcastChannel<UserState>('user-state');
  
  readonly state = this.channel.data;
  
  toggleTheme() {
    const current = this.state() || { isLoggedIn: true, theme: 'light' };
    this.channel.post({
      ...current,
      theme: current.theme === 'light' ? 'dark' : 'light',
    });
  }
}

SSR Compatibility

On the server, signals initialize with safe defaults:

  • datanull
  • errornull
  • isClosedfalse
  • post, close → no-op functions

Type Definitions

typescript
type BroadcastChannelOptions = WithInjector;

interface BroadcastChannelRef<T> {
  readonly data: Signal<T | null>;
  readonly error: Signal<MessageEvent | null>;
  readonly isClosed: Signal<boolean>;
  readonly post: (data: T) => void;
  readonly close: () => void;
}

function broadcastChannel<T>(
  name: string,
  options?: BroadcastChannelOptions,
): BroadcastChannelRef<T>;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23