REFERENCE
SDK Demos
How to use @asgard-js/core and @asgard-js/react, with demos you can try on the page.
Headless
If the built-in <Chatbot> doesn't fit your design, skip it entirely
and build your own UI on top of the useChannel hook. The chat panel
below is 100% custom — none of the SDK's stylesheets are imported.
Example
import { useMemo, useState } from "react";
import { AsgardServiceClient } from "@asgard-js/core";
import { useChannel } from "@asgard-js/react";
export function HeadlessChat() {
const client = useMemo(
() =>
new AsgardServiceClient({
botProviderEndpoint: "https://...",
}),
[],
);
const { conversation, sendMessage, isConnecting } = useChannel({
client,
customChannelId: "my-channel",
defaultIsOpen: true,
});
const messages = conversation
? Array.from(conversation.messages.values())
: [];
return (
<div>
{messages.map((msg) => {
if (msg.type === "user") return <div key={msg.messageId}>{msg.text}</div>;
if (msg.type === "bot") return <div key={msg.messageId}>{msg.message?.text}</div>;
if (msg.type === "tool-call")
return <div key={msg.messageId}>🔧 {msg.toolName}</div>;
return null;
})}
{/* Your own input → sendMessage({ text }) */}
</div>
);
}useChannelReturn ValueField Description conversationCurrent Conversationcontaining a messages MapsendMessageSend a message (supports text/payload/blobIds)resetChannelReset the channel and start a new conversation closeChannelClose the SSE connection channelTitleCurrent channel title ( null= unnamed); updates from the backendtitle.updateeventisConnectingWhether the channel is connecting isResettingWhether the channel is resetting Message Types
All messages share the
ConversationMessageunion — branch ontypewhen rendering:user— sent by the userbot— bot reply.bot.message.templatecarries the built-in SDK template; streaming text is onbot.typingText, the full text is onbot.message.texttool-call— agent tool invocationthinking— extended-thinking (reasoning) block:thinking.textholds the content,thinking.isThinkingdistinguishes streaming vs. completedsubagent— subagent lifecycle:subagent.kind(start/complete),status,summaryerror— connection or runtime error
Derived-State Hooks (tasks / subagents / title)
Besides walking
conversation.messagesyourself,@asgard-js/reactalso exposes three hooks that bridge the reactive derived-state stores onChannelintouseSyncExternalStore, re-rendering only when that slice changes:useTaskList(channel)→Task[](see Task Check List)useSubagents(channel)→Subagent[](see Subagent List)useChannelTitle(channel)→string | null(see Channel Title)
The framework-agnostic building blocks live in
@asgard-js/core:createDerivedStores(conversation$),deriveTasks(conversation),deriveSubagents(conversation).