Skip to content

Build a Chatbot

This guide shows a minimal React chatbot using @uclaw/sdk/react. useApp manages the app's agent directory; useAgent streams chat for the selected agent.

Prerequisites

Follow React Getting Started and create a backend endpoint that returns a short-lived client token.

Implementation

Create ChatBot.tsx:

tsx
import { useApp, useAgent } from "@uclaw/sdk/react";
import { useState } from "react";

function ActiveChat({ agentId }: { agentId: string }) {
  const [input, setInput] = useState("");
  const { chat, status } = useAgent({ agentId });

  function handleSubmit(event: React.FormEvent) {
    event.preventDefault();
    if (!input.trim() || chat.isStreaming) return;

    chat.sendMessage({
      role: "user",
      parts: [{ type: "text", text: input.trim() }],
    });
    setInput("");
  }

  return (
    <div className="chat-container">
      <header className="chat-header">
        <h3>UClaw Chatbot</h3>
        <span className={`status-badge ${status}`}>{status}</span>
      </header>

      <div className="chat-history">
        {chat.messages.map((message) => {
          const text = message.parts
            .filter((part) => part.type === "text")
            .map((part) => part.text)
            .join("");

          return (
            <div key={message.id} className={`chat-message ${message.role}`}>
              <strong>{message.role === "user" ? "You" : "AI"}:</strong>
              <p>{text}</p>
            </div>
          );
        })}
        {chat.isStreaming && <div className="loading">Agent is typing...</div>}
      </div>

      <form onSubmit={handleSubmit} className="chat-input-form">
        <input
          value={input}
          onChange={(event) => setInput(event.target.value)}
          placeholder="Ask a question..."
          disabled={chat.isStreaming}
        />
        <button type="submit" disabled={chat.isStreaming}>
          Send
        </button>
      </form>
    </div>
  );
}

export function ChatBot() {
  const [agentId, setAgentId] = useState<string | null>(null);
  const { createAgent } = useApp({});

  async function startChat() {
    const agent = await createAgent({
      title: "Chatbot",
      config: {
        instructions: "You are a helpful chatbot.",
        modelTier: "fast",
      },
    });
    setAgentId(agent.id);
  }

  if (!agentId) {
    return <button onClick={startChat}>Start chat</button>;
  }

  return <ActiveChat agentId={agentId} />;
}

Styling

css
.chat-container {
  display: flex;
  flex-direction: column;
  height: 500px;
  max-width: 600px;
  margin: 0 auto;
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 8px;
}
.chat-header {
  padding: 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  display: flex;
  justify-content: space-between;
}
.chat-history {
  flex: 1;
  overflow-y: auto;
  padding: 1rem;
}
.chat-message {
  margin-bottom: 1rem;
}
.chat-message.user {
  text-align: right;
}
.chat-input-form {
  display: flex;
  padding: 1rem;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.chat-input-form input {
  flex: 1;
  padding: 0.5rem;
}

Now you have a streaming chatbot connected to a stateful UClaw agent.