A Chatbot Widget

Combine a backend endpoint, conversation history, and streaming into an assistant widget.

A chatbot widget brings several pieces together: a backend endpoint, stored conversation history, and streaming for a responsive feel.

The parts

  • Frontend — a simple chat box that sends messages to your server and appends replies.
  • Backend — an endpoint that keeps the conversation history and calls Claude.
  • System prompt — defines the assistant's role and scope.

Keep it focused

Give the assistant a clear job in the system prompt ("help users with orders on this store") so it stays on-topic and useful.

Example

Example · javascript
// Minimal chatbot endpoint with per-session history and streaming
const sessions = new Map(); // sessionId -> messages[]

app.post("/api/chat", async (req, res) => {
  const { sessionId, message } = req.body;
  const history = sessions.get(sessionId) ?? [];
  history.push({ role: "user", content: message });

  res.setHeader("Content-Type", "text/plain; charset=utf-8");
  const stream = client.messages.stream({
    model: "claude-opus-4-8", max_tokens: 1024,
    system: "You are a support assistant for an online store. Be concise.",
    messages: history,
  });
  stream.on("text", (t) => res.write(t));
  const final = await stream.finalMessage();

  history.push({ role: "assistant", content: final.content });
  sessions.set(sessionId, history);
  res.end();
});

When to use it

  • Add a floating chatbot widget to your SaaS dashboard that streams Claude answers and remembers the session history.
  • Build a website assistant that uses a system prompt scoped to your product docs and streaming for responsiveness.
  • Embed a support bot in your React app that stores conversation history in useState and clears it on page refresh.

More examples

Backend endpoint for a chat message

Accepts the full messages array from the client so history is maintained by the browser and replayed each turn.

Example · javascript
// POST /api/chat — receives messages array, returns Claude's reply
export async function POST(req) {
  const { messages } = await req.json();
  const msg = await client.messages.create({
    model: "claude-haiku-4-5",
    max_tokens: 512,
    system: "You are a helpful assistant for AcmeCorp customers.",
    messages,
  });
  return Response.json({ reply: msg.content[0].text });
}

React state for conversation history

Keeps conversation history in React state, appending user and assistant turns before each API call.

Example · javascript
const [messages, setMessages] = useState([]);

async function sendMessage(text) {
  const next = [...messages, { role: "user", content: text }];
  setMessages(next);
  const res = await fetch("/api/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ messages: next }),
  });
  const { reply } = await res.json();
  setMessages(m => [...m, { role: "assistant", content: reply }]);
}

Stream replies into the chat bubble

Streams tokens into the last message bubble by updating state incrementally as each chunk arrives.

Example · javascript
async function sendStreamingMessage(text) {
  const next = [...messages, { role: "user", content: text }];
  setMessages([...next, { role: "assistant", content: "" }]);

  const res = await fetch("/api/chat-stream", {
    method: "POST",
    body: JSON.stringify({ messages: next }),
    headers: { "Content-Type": "application/json" },
  });
  const reader = res.body.getReader();
  const dec = new TextDecoder();
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    setMessages(m => [
      ...m.slice(0, -1),
      { role: "assistant", content: m.at(-1).content + dec.decode(value) },
    ]);
  }
}

Discussion

  • Be the first to comment on this lesson.