Skip to main content

Headless Usage

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.

Fully Custom UI

Skip the <Chatbot> component entirely. Use the useChannel hook to read the conversation and call sendMessage, then build whatever UI matches your brand.

The interface below is 100% custom — none of the SDK's built-in chat UI is used.

Loading...

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>
);
}

useChannel Return Value

FieldDescription
conversationCurrent Conversation containing a messages Map
sendMessageSend a message (supports text / payload / blobIds)
resetChannelReset the channel and start a new conversation
closeChannelClose the SSE connection
isConnectingWhether the channel is connecting
isResettingWhether the channel is resetting

Message Types

All messages share the ConversationMessage union — branch on type when rendering:

  • user — sent by the user
  • bot — bot reply; access SDK template data via message.template
  • tool-call — agent tool invocation
  • error — connection or runtime error