@langchain/angular ships injectStream together with a small family of companion selector hooks. The root hook gives you always-on access to thread state, messages, tool calls, and interrupts; the selector hooks open ref-counted subscriptions for the things that aren't needed on every view (per-subagent messages, media streams, submission queue, message metadata, raw channels).
Learn more: For end-to-end tutorials and UI patterns, see the LangChain frontend documentation.
npm install @langchain/angular @langchain/core
Peer dependencies: @angular/core (^19 || ^20) and @langchain/core.
Mount injectStream once per thread. Point it at a running agent server and render messages:
import { Component, inject } from "@angular/core";
import { injectStream } from "@langchain/angular";
@Component({
/* ... */
})
export class Chat {
readonly stream = injectStream({
assistantId: "agent",
apiUrl: "http://localhost:2024",
});
readonly messages = this.stream.messages;
readonly isLoading = this.stream.isLoading;
send() {
void this.stream.submit({ messages: [{ type: "human", content: "Hello!" }] });
}
}
Use the graph name from your langgraph.json as assistantId.
@langchain/angular splits the surface into two layers:
injectStream). Owns the thread lifecycle, the transport, and a handful of always-on projections: values, messages, toolCalls, interrupts, error, isLoading, and the subagent / subgraph discovery maps. Mount it once per thread.inject* selectors. Each opens a ref-counted subscription when the first component mounts it and releases it when the last consumer unmounts. Use them for anything scoped to a namespace, a subagent / subgraph, a specific message, an extension channel, or a media stream.import { injectStream, injectMessages, injectSubmissionQueue } from "@langchain/angular";
function Chat() {
const stream = injectStream({ assistantId: "agent", apiUrl: "/api" });
// Root: free read, no new subscription.
const messages = injectMessages(stream); // same as stream.messages
// Scoped: opens a subscription on mount.
const queue = injectSubmissionQueue(stream);
}
The key idea: views that don't render a subagent's messages never pay for that subagent's wire traffic. Subscriptions follow the injector tree.
The streaming cookbook collects end-to-end, runnable apps built on these hooks. Clone it and run any example from the typescript/ workspace:
| Example | Shows |
|---|---|
ui-angular |
A chat app with browser reconnect / replay — refresh mid-stream and reattach. |
multimodal |
Streaming text, images, audio, and video from scoped graph nodes. |
a2ui |
Generative UI rendered from a custom:a2ui extension channel. |
react-custom-transport |
Wiring injectStream to your own backend transport. |
streaming |
Terminal scripts for messages, subgraphs, subagents, HITL, and more. |
injectStreamEvery option, callback, and return value on the root injector.
Scoped state, submission queue, message metadata, and raw events.
Pause for human approval, edits, or headless tool resolution.
Edit a turn or retry from an earlier checkpoint.
Visualize subagents and nested subgraphs with lazy subscriptions.
Stream audio, images, video, and files from scoped graph nodes.
Connect to Agent Server (SSE/WebSocket) or your own backend adapter.
Full type inference from typeof agent and shared stream types.