
Soverius AI is now maintaining @copilotkit/angular, the Angular bindings for CopilotKit. We work directly with the CopilotKit team, and the code lives upstream in CopilotKit/CopilotKit — not in a fork.
This gives Angular developers most of the agentic building blocks the React community already has: a drop-in chat surface, generative UI, sandboxed "open generative UI," frontend tools, and shared agent state. All of it MIT-licensed. To show how it works, we built MacroQuest, an Angular + NgRx Signal Store food tracker that turns a meal description into an interactive, agent-generated UI.
Generative and agentic UI has mostly happened in React so far, and the unspoken assumption became "if you want an AI surface, bring a React app." We don't think that trade is necessary. Angular already has good primitives for this: signals for fine-grained reactive state, standalone components for composition, and a DI system that makes wiring an agent runtime into an app feel native.
At Soverius, we take local LLMs the whole way through — from the model on your hardware to the person using the app. That last step, the UI, shouldn't be the one that suddenly needs a cloud.
CopilotKit lists Angular as supported. What was missing was someone to make that real and keep it current. That's the job we've taken on: moving the Angular SDK toward the React v2 experience and keeping it aligned with core.
<copilot-chat />): compose, send, message actions, suggestions, and attachments, all owned by the SDKprovideCopilotKit(...)): runtime URL, A2UI, open generative UI, and labels, configured in your app configregisterRenderToolCall(...)): the agent calls a tool and your Angular component renders in the threadcpk-a2ui-surface): the agent authors a component tree and Angular paints it in the chat streamopenGenerativeUI.sandboxFunctions): the agent generates a sandboxed UI that calls back into your appregisterFrontendTool(...)): typed, narrow mutations the agent is allowed to callregisterHumanInTheLoop(...)): pauses the run and waits for a person to approveinjectAgentStore(...)): the agent's state as a signal, both sides read and writeconnectAgentContext(...)): your app's state exposed to the agent, read-only[attachments]): images, audio, video, and documents through the chatThe package supports Angular 19–21 today. Angular 22 is next: our demo already runs on 22 to validate it ahead of the package. And it's framework-native — it composes with NgRx Signal Store, as the demo shows.
MacroQuest is a working app shell with daily goals, a live macro dashboard, recent meals, and a CopilotKit chat panel, backed by a local Gemma model through llama.cpp. The demo turns on two prompts, each exercising a different kind of generated UI.

Prompt 1 — a rich A2UI surface:
Analyze a grilled chicken bowl with rice and beans.
Gemma authors an A2UI component tree. The premade Angular renderer paints it as a meal card in the chat stream, and the draft lands in the store as the selected meal.

Prompt 2 — an Open Generative UI sandbox:
Suggest a lighter version of my current meal.
This time the agent generates an interactive UI inside a sandbox iframe. Its controls call back into the app through a sandbox function to apply the lighter swap, kept deliberately separate from trusted state.

The dashboard never re-renders from the agent's word — it recomputes from the store. The agent proposes; the store decides.
All of the code below is real code from the MacroQuest repo. Mounting the full chat experience is one component:
<copilot-chat [attachments]="store.mealPhotoAttachments" />Wiring the runtime is declarative app config, including the sandbox function that Prompt 2's generated UI calls back into:
provideCopilotKit({
runtimeUrl: '/api/copilotkit',
a2ui: { includeSchema: true },
openGenerativeUI: {
sandboxFunctions: [
{
name: 'applyMacroSwap',
description:
'Apply a generated lighter macro swap to the selected MacroQuest meal.',
parameters: macroSwapSandboxSchema,
handler: applyMacroSwapFromSandbox,
},
],
},
});The agent integration itself lives next to your state, not scattered through components. In MacroQuest it's an NgRx signalStoreFeature called withCopilot(), whose onInit hook does two things. First, it exposes trusted state to the agent — a computed over store signals, so the context the agent sees is always current:
const agentContext = computed(() => ({
description:
'MacroQuest trusted Angular state from NgRx Signal Store. ' +
'Use this context before proposing meal updates.',
value: JSON.stringify({
activeDate: store.activeDate(),
goals: store.goals(),
dailyTotals: store.dailyTotals(),
remaining: store.remaining(),
selectedMeal: store.selectedMeal(),
}),
}));
connectAgentContext(agentContext);Second, it registers the tools the agent may call. Here's setMacroGoals in full — Zod schema, handler, and all:
registerFrontendTool<MacroGoalsToolArgs>({
name: 'setMacroGoals',
description: 'Update MacroQuest daily macro goals in the Angular NgRx Signal Store.',
parameters: z.object({
calories: z.number().optional(),
protein: z.number().optional(),
carbs: z.number().optional(),
fat: z.number().optional(),
}),
handler: async (goals) => {
store.setGoals(goals);
return { ok: true, goals: store.goals() };
},
});Its sibling logMealDraft follows the same pattern with a richer schema for food items. Note what the tool is: a narrow, typed mutation into state you control. The agent can't reach anything else.
The sandbox side is even more constrained. The generated UI runs in an iframe and can only call the functions you listed in sandboxFunctions. The handler is a plain module function that refuses to act when the app isn't in a state to accept the change:
export async function applyMacroSwapFromSandbox(input: MacroSwapSandboxArgs) {
if (!sandboxStore) {
return { ok: false, message: 'MacroQuest is not ready yet.' };
}
const meal = sandboxStore.applyMacroSwap(input);
if (!meal) {
return { ok: false, message: 'Analyze or select a meal before applying a sandbox swap.' };
}
return { ok: true, mealId: meal.id, title: meal.title, totals: getMealTotals(meal) };
}That's the whole shape: declarative provider config, one chat component, and a thin, typed boundary between the agent and your state. The full technical deep-dive walks the entire build.
@copilotkit/angular releases alongside the CopilotKit core version line.main. Stewardship, not a fork.Honest status: Angular isn't widely used in CopilotKit production yet, and there are gaps versus React — tests, CI, docs, API surface. During this catch-up phase, expect occasional breaking changes.
The Angular work is merged into CopilotKit main:
6a768ab7d), chat and transcription improvements (ebcbb19ea), OpenRouter support (800b55dac).b5fa76d5f) and hardened the A2UI renderer (10245a35f).MealScanCard, MacroRing) over the basic catalog.Follow CopilotKit on Twitter. If you run into problems getting started, reach out to us in the CopilotKit or AG-UI communities.



Subscribe to our blog and get updates on CopilotKit in your inbox.