Yak Docs
SDKs

Angular SDK

The @yak-io/angular package provides an Angular-compatible provider for integrating Yak. It exposes getter-based reactive state, a state subscription API, and explicit mount()/destroy() methods for lifecycle control.

Installation

npm install @yak-io/angular @yak-io/javascript
pnpm add @yak-io/angular @yak-io/javascript
yarn add @yak-io/angular @yak-io/javascript
bun add @yak-io/angular @yak-io/javascript

Quick Start

Create a Yak service

Wrap the provider in an Angular service for dependency injection:

// yak.service.ts
import { Injectable, OnDestroy } from "@angular/core";
import { createYakProvider, type YakApi } from "@yak-io/angular";

@Injectable({ providedIn: "root" })
export class YakService implements OnDestroy {
  private yak: YakApi;

  readonly isOpen: YakApi["isOpen"];
  readonly isReady: YakApi["isReady"];

  constructor() {
    this.yak = createYakProvider({
      appId: "your-app-id",
      getConfig: async () => {
        const res = await fetch("/api/yak");
        return res.json();
      },
      onToolCall: async (name, args) => {
        const res = await fetch("/api/yak", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ name, args }),
        });
        const data = await res.json();
        if (!data.ok) throw new Error(data.error);
        return data.result;
      },
    });

    // Expose reactive getters
    Object.defineProperty(this, "isOpen", {
      get: () => this.yak.isOpen,
    });
    Object.defineProperty(this, "isReady", {
      get: () => this.yak.isReady,
    });
  }

  mount() {
    this.yak.mount();
  }

  ngOnDestroy() {
    this.yak.destroy();
  }

  open() {
    this.yak.open();
  }

  close() {
    this.yak.close();
  }

  openWithPrompt(prompt: string) {
    this.yak.openWithPrompt(prompt);
  }

  subscribeToToolEvents(handler: (event: import("@yak-io/angular").ToolCallEvent) => void) {
    return this.yak.subscribeToToolEvents(handler);
  }

  subscribeToState(handler: (state: { isOpen: boolean; isReady: boolean }) => void) {
    return this.yak.subscribeToState(handler);
  }
}

Mount in your root component

// app.component.ts
import { Component, OnInit, OnDestroy } from "@angular/core";
import { YakService } from "./yak.service";

@Component({
  selector: "app-root",
  template: `
    <router-outlet />
    <button (click)="yakService.open()">Open Chat</button>
  `,
})
export class AppComponent implements OnInit, OnDestroy {
  constructor(public yakService: YakService) {}

  ngOnInit() {
    this.yakService.mount();
  }

  ngOnDestroy() {
    this.yakService.ngOnDestroy();
  }
}

Set up server handlers

Use @yak-io/javascript to create the API endpoints on your backend. See the JavaScript SDK for runtime-specific examples (Express, Hono, etc.).

// api/yak.ts
import { createYakHandler } from "@yak-io/javascript/server";

export const { GET, POST } = createYakHandler({
  routes: [
    { path: "/", title: "Home" },
    { path: "/products", title: "Products" },
  ],
});

API

createYakProvider

Creates a Yak widget instance with plain getter-based reactive state. Use inside an Angular service or component.

OptionTypeRequiredDescription
appIdstringYesYour Yak application ID
mode"chat" | "voice" | "both"NoWhich surfaces the trigger exposes. Defaults to "chat". See Voice Mode.
getConfig() => Promise<ChatConfig> | ChatConfigNoConfig provider for routes and tools (used by chat and voice)
onToolCall(name, args) => Promise<unknown>NoHandler for tool execution (used by chat and voice)
themeThemeNoWidget styling options
onRedirect(path: string) => voidNoCustom navigation handler
disableRestartButtonbooleanNoHide the restart button in the header
disablePageContentbooleanNoStop sending any page context (URL, title, and visible text) to the assistant. The widget still works, but it won't be aware of the page the user is on.
triggerboolean | TriggerButtonConfigNoConfigure the floating trigger button
user{ id, hash }NoSigned end-user identity. Enables conversation persistence and history. Call setUser() to change it after setup (login/logout).

Privacy: By default Yak shares the current page's URL, title, and visible text with the assistant so it can answer questions about the page the user is viewing. Set disablePageContent to turn this off entirely — the SDK then sends nothing about the page (not even the URL), so the assistant can't answer page-specific questions.

Return value (YakApi)

PropertyTypeDescription
isOpenboolean (getter)Whether the chat panel is currently open
isReadyboolean (getter)Whether the widget iframe is ready
chatLoadingboolean (getter)isOpen && !isReady — opening but not yet interactive
voiceMachineVoiceMachine (getter)Current voice state — see Voice Mode
voiceLoadingboolean (getter)true while the voice session is connecting
open() => voidOpen the chat panel
close() => voidClose the chat panel
openWithPrompt(prompt: string) => voidOpen and send a specific prompt
voiceStart() => Promise<void>Start a voice session
voiceStop() => Promise<void>Stop the current voice session
voiceToggle() => Promise<void>Start if idle/error, stop if active
setUser(user?: { id, hash }) => voidSet or clear the signed end-user identity after setup — call on login/logout
subscribeToToolEvents(handler) => () => voidSubscribe to tool call events (returns unsubscribe)
subscribeToState(handler) => () => voidSubscribe to combined chat + voice state changes (returns unsubscribe)
mount() => voidMount the widget DOM — call in ngOnInit
destroy() => voidDestroy the widget DOM — call in ngOnDestroy

Reactive State

Use subscribeToState for reactive updates in Angular templates:

import { Component, OnInit, OnDestroy, signal } from "@angular/core";
import { YakService } from "./yak.service";

@Component({
  selector: "app-chat-button",
  template: `
    <button (click)="yakService.open()">
      {{ isOpen() ? "Close Chat" : "Open Chat" }}
    </button>
  `,
})
export class ChatButtonComponent implements OnInit, OnDestroy {
  isOpen = signal(false);
  private unsubscribe?: () => void;

  constructor(public yakService: YakService) {}

  ngOnInit() {
    this.unsubscribe = this.yakService.subscribeToState((state) => {
      this.isOpen.set(state.isOpen);
    });
  }

  ngOnDestroy() {
    this.unsubscribe?.();
  }
}

Tool Events

Subscribe to tool call completion events for UI synchronization:

import { Component, OnInit, OnDestroy } from "@angular/core";
import { YakService } from "./yak.service";

@Component({
  selector: "app-orders",
  template: `<!-- order list -->`,
})
export class OrdersComponent implements OnInit, OnDestroy {
  private unsubscribe?: () => void;

  constructor(private yakService: YakService) {}

  ngOnInit() {
    this.unsubscribe = this.yakService.subscribeToToolEvents((event) => {
      if (event.ok && event.name.startsWith("order.")) {
        this.refreshOrders();
      }
    });
  }

  ngOnDestroy() {
    this.unsubscribe?.();
  }

  private refreshOrders() {
    // re-fetch order data
  }
}

Router Integration

Pass Angular Router's navigate for client-side navigation:

import { Injectable, OnDestroy } from "@angular/core";
import { Router } from "@angular/router";
import { createYakProvider, type YakApi } from "@yak-io/angular";

@Injectable({ providedIn: "root" })
export class YakService implements OnDestroy {
  private yak: YakApi;

  constructor(private router: Router) {
    this.yak = createYakProvider({
      appId: "your-app-id",
      onRedirect: (path) => this.router.navigateByUrl(path),
      // ...other options
    });
  }

  // ...
}

On this page