@tanstack/ai-angular
v0.0.1
Published
Angular signals integration for TanStack AI streaming chat, structured outputs, and media generation.
Maintainers
Readme
TanStack AI — Angular
Angular signal-based bindings for TanStack AI — streaming chat, tool-calling agents, and media generation built on Angular's native reactivity model.
Read the docs ->
Install
pnpm add @tanstack/ai-angular @tanstack/ainpm install @tanstack/ai-angular @tanstack/aiMinimal Usage
Important: All
inject*functions use Angular's dependency injection system and must be called within an Angular injection context — a component or directive class field initializer, the constructor, or insiderunInInjectionContext. Calling them outside an injection context will throw a runtime error.
The example below shows a standalone component that streams chat messages from a server endpoint via SSE:
import { Component } from '@angular/core'
import { CommonModule } from '@angular/common'
import { injectChat } from '@tanstack/ai-angular'
import { fetchServerSentEvents } from '@tanstack/ai-client'
@Component({
selector: 'app-chat',
standalone: true,
imports: [CommonModule],
template: `
<ul>
@for (message of chat.messages(); track message.id) {
<li>{{ message.role }}: {{ message.content }}</li>
}
</ul>
<input #input placeholder="Type a message..." />
<button (click)="chat.sendMessage(input.value); input.value = ''">
Send
</button>
@if (chat.isLoading()) {
<p>Thinking...</p>
}
`,
})
export class ChatComponent {
// injectChat is called in a field initializer — this is a valid injection context.
chat = injectChat({
connection: fetchServerSentEvents('/api/chat'),
})
}All state is exposed as Angular Signals. Read them by calling them as functions:
| Signal | Type | Description |
| ------------------------- | -------------------- | ------------------------------------------- |
| chat.messages() | UIMessage[] | Current message list |
| chat.isLoading() | boolean | Whether a response is streaming |
| chat.error() | Error \| undefined | Last error, if any |
| chat.status() | ChatClientState | 'ready', 'streaming', 'error', ... |
| chat.isSubscribed() | boolean | Whether a live (SSE push) session is active |
| chat.connectionStatus() | ConnectionStatus | Transport connection status |
Available methods on the return value: sendMessage, append, reload, stop, clear, setMessages, addToolResult, addToolApprovalResponse.
Server endpoint
The client pairs with any endpoint that returns a TanStack AI SSE stream:
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
export async function POST(request: Request) {
const body = await request.json()
const stream = chat({
adapter: openaiText('gpt-5.5'),
messages: body.messages,
})
return toServerSentEventsResponse(stream)
}Available Functions
| Function | Description |
| ---------------------- | ---------------------------------------------------------------- |
| injectChat | Streaming chat with messages, tool calls, and structured outputs |
| injectGeneration | Generic generation client (streaming or one-shot) |
| injectGenerateImage | Image generation |
| injectGenerateAudio | Audio generation |
| injectGenerateVideo | Video generation |
| injectGenerateSpeech | Text-to-speech |
| injectSummarize | Summarization |
| injectTranscription | Audio transcription |
All generation functions return signals (result, isLoading, error, status) and methods (generate, stop, reset).
Injection Context
Angular's DI system requires that inject() is called during component construction. Every inject* function in this package calls inject() internally. Valid call sites:
// Field initializer (recommended)
export class MyComponent {
chat = injectChat({ connection: fetchServerSentEvents('/api/chat') })
}
// Constructor
export class MyComponent {
chat: ReturnType<typeof injectChat>
constructor() {
this.chat = injectChat({ connection: fetchServerSentEvents('/api/chat') })
}
}
// Inside runInInjectionContext
const chat = runInInjectionContext(injector, () =>
injectChat({ connection: fetchServerSentEvents('/api/chat') }),
)Get Involved
- Read the docs.
- Participate in GitHub discussions.
- Chat with the community on Discord.
- See CONTRIBUTING.md for setup instructions.
- Become a sponsor.
