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
getConfig() => Promise<ChatConfig> | ChatConfigNoConfig provider for routes and tools
onToolCall(name, args) => Promise<unknown>NoHandler for tool execution
themeThemeNoWidget styling options
onRedirect(path: string) => voidNoCustom navigation handler
disableRestartButtonbooleanNoHide the restart button in the header
triggerboolean | TriggerButtonConfigNoConfigure the floating trigger button

Return value (YakApi)

PropertyTypeDescription
isOpenboolean (getter)Whether the chat panel is currently open
isReadyboolean (getter)Whether the widget iframe is ready
open() => voidOpen the chat panel
close() => voidClose the chat panel
openWithPrompt(prompt: string) => voidOpen and send a specific prompt
subscribeToToolEvents(handler) => () => voidSubscribe to tool call events (returns unsubscribe)
subscribeToState(handler) => () => voidSubscribe to 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