Skip to main content
CopilotKit provides a complete Angular SDK with services, directives, and components to build AI-powered applications. The Angular integration uses dependency injection and follows Angular best practices.

Installation

1

Install the package

Install the Angular package and its peer dependencies:
npm install @copilotkitnext/angular
# Peer dependencies (if not already installed)
npm install @angular/cdk rxjs
2

Import styles

Add CopilotKit styles to your angular.json or import in your global styles:
{
  "styles": [
    "node_modules/@copilotkitnext/angular/styles.css",
    "src/styles.css"
  ]
}
Or in your styles.css:
@import "@copilotkitnext/angular/styles.css";
3

Configure providers

Set up CopilotKit providers in your application configuration:
import { ApplicationConfig } from "@angular/core";
import { provideCopilotKit } from "@copilotkitnext/angular";

export const appConfig: ApplicationConfig = {
  providers: [
    provideCopilotKit({
      runtimeUrl: "http://localhost:3001/api/copilotkit",
    }),
  ],
};

Core Concepts

Configuration Provider

The provideCopilotKit function configures the CopilotKit service with your runtime settings:
import { ApplicationConfig } from "@angular/core";
import {
  provideCopilotKit,
  provideCopilotChatLabels,
} from "@copilotkitnext/angular";

export const appConfig: ApplicationConfig = {
  providers: [
    provideCopilotKit({
      runtimeUrl: "http://localhost:3001/api/copilotkit",
      headers: {
        Authorization: "Bearer your-token",
      },
      licenseKey: "ck_pub_...",
      properties: {
        userId: "user-123",
      },
      frontendTools: [],
      humanInTheLoop: [],
    }),
    provideCopilotChatLabels({
      chatInputPlaceholder: "Ask me anything...",
      chatDisclaimerText: "AI responses may need verification.",
    }),
  ],
};

Configuration Options

  • runtimeUrl - URL of your CopilotKit runtime endpoint
  • headers - Custom headers to include in requests
  • licenseKey - CopilotCloud license key
  • properties - Additional properties to send with requests
  • agents - Local agents (development only)
  • selfManagedAgents - Self-managed agent instances
  • frontendTools - Array of frontend tool configurations
  • humanInTheLoop - Array of human-in-the-loop tool configurations
  • renderToolCalls - Array of tool call render configurations

CopilotKit Service

The CopilotKit service is the core service that manages the connection to your runtime. Inject it into your components or services:
import { Component, inject } from "@angular/core";
import { CopilotKit } from "@copilotkitnext/angular";

@Component({
  selector: "app-my-component",
  template: `
    <div>
      <p>Runtime Status: {{ copilotKit.runtimeConnectionStatus() }}</p>
      <p>Runtime URL: {{ copilotKit.runtimeUrl() }}</p>
    </div>
  `,
})
export class MyComponent {
  copilotKit = inject(CopilotKit);

  ngOnInit() {
    // Access the core CopilotKit instance
    const core = this.copilotKit.core;
    
    // Update runtime configuration
    this.copilotKit.updateRuntime({
      runtimeUrl: "http://new-url/api/copilotkit",
      headers: { "X-Custom": "value" },
    });
  }
}

Frontend Tools

Register frontend tools using the CopilotKit service. Tools can be added at the application level or dynamically in components:
import { Component, inject, Injector } from "@angular/core";
import { CopilotKit } from "@copilotkitnext/angular";
import { z } from "zod";

@Component({
  selector: "app-tool-demo",
  template: `
    <div>
      <button (click)="incrementCounter()">Increment: {{ count }}</button>
    </div>
  `,
})
export class ToolDemoComponent {
  copilotKit = inject(CopilotKit);
  injector = inject(Injector);
  count = 0;

  ngOnInit() {
    // Register a frontend tool
    this.copilotKit.addFrontendTool({
      name: "incrementCounter",
      description: "Increment the counter by a specified amount",
      parameters: z.object({
        amount: z.number().describe("Amount to increment"),
      }),
      handler: (args) => {
        this.count += args.amount;
        return `Counter incremented by ${args.amount}`;
      },
      injector: this.injector,
    });
  }

  ngOnDestroy() {
    // Clean up tool when component is destroyed
    this.copilotKit.removeTool("incrementCounter");
  }

  incrementCounter() {
    this.count++;
  }
}

Tool Call Rendering

Render custom UI for tool executions by creating a component and registering it:
import { Component, input } from "@angular/core";

@Component({
  selector: "app-search-tool-render",
  template: `
    <div class="search-tool-call">
      <h3>Searching for: {{ args().query }}</h3>
      @if (args().limit) {
        <p>Limit: {{ args().limit }} results</p>
      }
      <p>Status: {{ status() }}</p>
    </div>
  `,
  styles: [`
    .search-tool-call {
      padding: 12px;
      border: 1px solid #ccc;
      border-radius: 8px;
      margin: 8px 0;
    }
  `],
})
export class SearchToolRenderComponent {
  name = input.required<string>();
  args = input.required<{ query: string; limit?: number }>();
  status = input.required<string>();
}
Register the renderer in your configuration:
import { ApplicationConfig } from "@angular/core";
import { provideCopilotKit } from "@copilotkitnext/angular";
import { SearchToolRenderComponent } from "./components/search-tool-render";
import { z } from "zod";

export const appConfig: ApplicationConfig = {
  providers: [
    provideCopilotKit({
      runtimeUrl: "http://localhost:3001/api/copilotkit",
      renderToolCalls: [
        {
          name: "search",
          args: z.object({
            query: z.string(),
            limit: z.number().optional(),
          }),
          component: SearchToolRenderComponent,
        },
      ],
    }),
  ],
};
For wildcard rendering (any undefined tool):
@Component({
  selector: "app-wildcard-tool-render",
  template: `
    <div class="unknown-tool">
      <strong>Tool: {{ name() }}</strong>
      <pre>{{ args() | json }}</pre>
      <p>Status: {{ status() }}</p>
    </div>
  `,
})
export class WildcardToolRenderComponent {
  name = input.required<string>();
  args = input.required<any>();
  status = input.required<string>();
}

// In config:
renderToolCalls: [
  {
    name: "*",
    component: WildcardToolRenderComponent,
  } as any,
]

Human-in-the-Loop

Add approval flows to tools using the human-in-the-loop pattern:
import { Component } from "@angular/core";

@Component({
  selector: "app-delete-approval",
  template: `
    <div class="approval-ui">
      <p>Approve deletion of item: {{ args().itemId }}</p>
      <button (click)="approve()">Approve</button>
      <button (click)="reject()">Reject</button>
    </div>
  `,
})
export class DeleteApprovalComponent {
  args = input.required<{ itemId: string }>();
  onApprove = input.required<(result: any) => void>();
  onReject = input.required<(reason: string) => void>();

  approve() {
    this.onApprove()({ success: true });
  }

  reject() {
    this.onReject()("User rejected deletion");
  }
}
Register in configuration:
import { z } from "zod";

provideCopilotKit({
  runtimeUrl: "http://localhost:3001/api/copilotkit",
  humanInTheLoop: [
    {
      name: "deleteItem",
      description: "Delete an item (requires approval)",
      parameters: z.object({
        itemId: z.string(),
      }),
      component: DeleteApprovalComponent,
    },
  ],
})

Chat Components

CopilotChat

The main chat interface component:
import { Component } from "@angular/core";
import { CopilotChat } from "@copilotkitnext/angular";

@Component({
  selector: "app-chat",
  standalone: true,
  imports: [CopilotChat],
  template: `
    <div style="height: 100vh;">
      <copilot-chat [threadId]="'thread-123'"></copilot-chat>
    </div>
  `,
})
export class ChatComponent {}

CopilotChatView

A lower-level chat view component with more customization options:
import { Component } from "@angular/core";
import { CopilotChatView } from "@copilotkitnext/angular";

@Component({
  selector: "app-chat-view",
  standalone: true,
  imports: [CopilotChatView],
  template: `
    <copilot-chat-view
      [threadId]="threadId"
      [agentId]="agentId"
      [className]="'custom-chat-class'"
    ></copilot-chat-view>
  `,
})
export class ChatViewComponent {
  threadId = "my-thread";
  agentId = "my-agent";
}

Directives

copilotKitAgentContext

Provide dynamic context to your agent using the directive:
import { Component } from "@angular/core";
import { CopilotKitAgentContextDirective } from "@copilotkitnext/angular";

@Component({
  selector: "app-context-demo",
  standalone: true,
  imports: [CopilotKitAgentContextDirective],
  template: `
    <div
      [copilotKitAgentContext]="{
        description: 'Current user information',
        value: currentUser
      }"
    >
      <p>Logged in as: {{ currentUser.name }}</p>
    </div>
  `,
})
export class ContextDemoComponent {
  currentUser = {
    id: "user-123",
    name: "John Doe",
    email: "john@example.com",
  };
}

Complete Example

Here’s a full example of a standalone Angular component with CopilotKit:
import { Component, inject, Injector } from "@angular/core";
import { CommonModule } from "@angular/common";
import {
  CopilotChat,
  CopilotKit,
  CopilotKitAgentContextDirective,
} from "@copilotkitnext/angular";
import { z } from "zod";

@Component({
  selector: "app-counter-chat",
  standalone: true,
  imports: [
    CommonModule,
    CopilotChat,
    CopilotKitAgentContextDirective,
  ],
  template: `
    <div style="display: flex; height: 100vh;">
      <div
        style="flex: 1; padding: 20px;"
        [copilotKitAgentContext]="{
          description: 'Current counter value',
          value: count
        }"
      >
        <h1>Counter: {{ count }}</h1>
        <button (click)="increment()">Increment</button>
        <button (click)="reset()">Reset</button>
      </div>
      
      <div style="flex: 1;">
        <copilot-chat [threadId]="'counter-thread'"></copilot-chat>
      </div>
    </div>
  `,
})
export class CounterChatComponent {
  copilotKit = inject(CopilotKit);
  injector = inject(Injector);
  count = 0;

  ngOnInit() {
    // Register increment tool
    this.copilotKit.addFrontendTool({
      name: "incrementCounter",
      description: "Increment the counter by a specified amount",
      parameters: z.object({
        amount: z.number().describe("Amount to increment"),
      }),
      handler: (args) => {
        this.count += args.amount;
        return `Counter incremented by ${args.amount}. New value: ${this.count}`;
      },
      injector: this.injector,
    });

    // Register reset tool
    this.copilotKit.addFrontendTool({
      name: "resetCounter",
      description: "Reset the counter to zero",
      parameters: z.object({}),
      handler: () => {
        this.count = 0;
        return "Counter reset to 0";
      },
      injector: this.injector,
    });
  }

  ngOnDestroy() {
    this.copilotKit.removeTool("incrementCounter");
    this.copilotKit.removeTool("resetCounter");
  }

  increment() {
    this.count++;
  }

  reset() {
    this.count = 0;
  }
}

Reactive Programming with RxJS

The CopilotKit service uses Angular Signals, but you can convert them to Observables when needed:
import { Component, inject } from "@angular/core";
import { toObservable } from "@angular/core/rxjs-interop";
import { CopilotKit } from "@copilotkitnext/angular";

@Component({
  selector: "app-reactive-demo",
  template: `<div>Status: {{ status$ | async }}</div>`,
})
export class ReactiveDemoComponent {
  copilotKit = inject(CopilotKit);
  
  status$ = toObservable(this.copilotKit.runtimeConnectionStatus);
  
  ngOnInit() {
    this.status$.subscribe(status => {
      console.log("Connection status changed:", status);
    });
  }
}

TypeScript Support

CopilotKit Angular is fully typed. Import types for custom implementations:
import type {
  CopilotKitConfig,
  FrontendToolConfig,
  HumanInTheLoopConfig,
  RenderToolCallConfig,
} from "@copilotkitnext/angular";

Next Steps

Human-in-the-Loop

Learn how to add approval flows to your tools

Tool Development

Build custom tools for your agents

Agent Configuration

Configure and customize your agents