UI Integration
SkillAI & modelsWire agents into React, Vue, and Svelte frontends with streaming hooks, and set up server-side Next.js App Router or Express API routes using AgentStream.toSSE().
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the UI Integration skill
What this skill tells your AI
The instructions your AI receives, as published by tylerjrbuell/reactive-agents-ts in apps/docs/skills/ui-integration/SKILL.md and read by ahel’s review.
Agent objective
Produce a working server-side API route using AgentStream.toSSE() and a client-side component using the appropriate framework hook, connected end-to-end.
When to load this skill
- Building a chat UI or streaming text interface in React, Vue, or Svelte
- Wiring an agent into a Next.js App Router API route
- Connecting an agent to an Express or Elysia HTTP server
- Needing real-time token-by-token streaming to the browser
Implementation baseline
Server — Next.js App Router
// app/api/agent/route.ts
import { ReactiveAgents, AgentStream } from "@reactive-agents/runtime";
export async function POST(req: Request) {
const { prompt } = await req.json();
const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withReasoning({ defaultStrategy: "adaptive", maxIterations: 10 })
.withTools({ allowedTools: ["web-search", "checkpoint"] })
.build();
return AgentStream.toSSE(agent.runStream(prompt));
}
Client — React
import { useAgentStream } from "@reactive-agents/react";
function Chat() {
const { text, status, error, run, cancel } = useAgentStream("/api/agent");
return (
<div>
<button onClick={() => run("Explain quantum entanglement")} disabled={status === "streaming"}>
Ask
</button>
{status === "streaming" && <button onClick={cancel}>Stop</button>}
<p>{text}</p>
{error && <p style={{ color: "red" }}>{error.message}</p>}
</div>
);
}
Key patterns
React hooks
import { useAgentStream, useAgent } from "@reactive-agents/react";
// Streaming — token-by-token delivery
const {
text, // string — accumulated text so far
events, // AgentStreamEvent[] — all events received
status, // "idle" | "streaming" | "completed" | "error"
error, // Error | null
output, // string | null — final output (set on completion)
run, // (prompt: string) => void — start the agent
cancel, // () => void — abort the stream
} = useAgentStream("/api/agent");
// One-shot — waits for full result
const {
output, // string | null — final output
loading, // boolean
error, // Error | null
run, // (prompt: string) => Promise<void>
} = useAgent("/api/agent");
Vue composables
<script setup lang="ts">
import { useAgentStream, useAgent } from "@reactive-agents/vue";
// Same return shape as React hooks
const { text, status, error, run, cancel } = useAgentStream("/api/agent");
const { output, loading, error: err, run: fetch } = useAgent("/api/agent");
</script>
<template>
<button @click="run('Explain AI')">Ask</button>
<p>{{ text }}</p>
</template>
Svelte stores
<script lang="ts">
import { createAgentStream, createAgent } from "@reactive-agents/svelte";
// Returns a Svelte readable store
const agent = createAgentStream("/api/agent");
// $agent: AgentStreamState = { text, status, error, run, cancel }
const oneShot = createAgent("/api/agent");
// $oneShot: AgentState = { output, loading, error, run }
</script>
<button on:click={() => $agent.run("Explain AI")}>Ask</button>
<p>{$agent.text}</p>
{#if $agent.status === "streaming"}<span>●</span>{/if}
Streaming density
// Default: "tokens" — TextDelta events only (minimal payload)
return AgentStream.toSSE(agent.runStream(prompt));
// Full: all events including phase changes and tool calls
return AgentStream.toSSE(agent.runStream(prompt, { density: "full" }));
// Use "full" density when the UI shows tool usage or reasoning steps
Express / Elysia server
import express from "express";
import { ReactiveAgents, AgentStream } from "@reactive-agents/runtime";
const app = express();
app.use(express.json());
app.post("/api/agent", async (req, res) => {
const { prompt } = req.body;
const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withReasoning({ defaultStrategy: "adaptive" })
.withTools()
.build();
const stream = agent.runStream(prompt);
for await (const event of stream) {
if (event.type === "TextDelta") {
res.write(`data: ${JSON.stringify(event)}\n\n`);
}
if (event.type === "StreamCompleted") break;
}
res.end();
});
Handling stream events client-side
const { events } = useAgentStream("/api/agent");
// Event types when density: "full":
// TextDelta — { type: "TextDelta", content: string }
// ToolCallStart — { type: "ToolCallStart", toolName: string }
// ToolCallResult — { type: "ToolCallResult", toolName: string, result: unknown }
// StreamCompleted — { type: "StreamCompleted", output: string }
// StreamError — { type: "StreamError", error: string }
// StreamCancelled — { type: "StreamCancelled" }
Packages
| Package | Exports |
|---|---|
@reactive-agents/react | useAgent, useAgentStream |
@reactive-agents/vue | useAgent, useAgentStream |
@reactive-agents/svelte | createAgent, createAgentStream |
@reactive-agents/runtime | AgentStream (server-side SSE adapter) |
Pitfalls
- The API route must be a server-side route — never build the agent in browser code (API keys would leak)
AgentStream.toSSE()returns aResponseobject compatible with the Web Fetch API — it works in Next.js App Router and edge runtimes directlydensity: "tokens"(default) only sendsTextDelta— usedensity: "full"if the client needs tool call visibilityuseAgent(one-shot) makes a POST and waits for JSON{ output: string }— the server route must return JSON, not SSE- Always build the agent inside the request handler (not at module level) — module-level agents persist state across requests
cancel()sends an AbortSignal to the server — the server route must pass theAbortSignaltoagent.runStream()for it to take effect
Signals
- GitHub stars
- 27
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
ui-integration- Source
- github.com/tylerjrbuell/reactive-agents-ts