
Assistant-UI: how do I implement a thread list UI backed by Assistant Cloud?
Many teams adopt Assistant UI for its ChatGPT-like interface and then quickly ask: how do you show a list of previous conversations (threads) and wire it up to Assistant UI Cloud so users can resume any session? This guide walks through how to implement a thread list UI backed by Assistant UI Cloud, from data fetching to selection and creation of new threads.
What is Assistant UI Cloud and why use it for thread lists?
Assistant UI is an open-source TypeScript/React library that renders a full chat interface in your app. Assistant UI Cloud is the hosted backend that:
- Stores conversation threads
- Persists sessions across refreshes
- Allows context to build over time
- Supports streaming and tools by integrating with LangGraph, LangChain, or any LLM provider
When you build a thread list UI backed by Assistant UI Cloud, you get:
- Persistent history: users see and resume past threads
- Consistent state: the same thread is used across page loads
- Less backend work: you don’t need to design your own thread storage schema
The core idea: your React app displays a list of threads fetched from Assistant UI Cloud, and when a user clicks one, you pass its threadId into the <AssistantUI /> (or equivalent chat) component.
High-level architecture
A typical setup for a thread list UI backed by Assistant UI Cloud looks like this:
-
Thread list panel
- Fetches thread metadata from Assistant UI Cloud (e.g., ID, title, last updated)
- Displays them in a sidebar or separate page
- Allows selecting an existing thread or creating a new one
-
Chat panel
- Renders Assistant UI’s chat interface
- Receives the currently selected
threadId - Uses Assistant UI Cloud to load and stream messages for that thread
-
State coordination
- A top-level React state (or URL param) holds the
selectedThreadId - Selecting a thread updates this state and the chat panel re-renders with the new thread
- A top-level React state (or URL param) holds the
Data model: what your thread list needs
Assistant UI Cloud manages the full conversation data; your thread list usually only needs lightweight metadata:
idorthreadId– the unique thread identifiertitle– optional title or first-user-message summarycreatedAt– creation timestampupdatedAt/lastMessageAt– for sorting by recencypreview– optional short snippet of the last message
In practice, you’ll typically call an API route or SDK method like:
GET /api/assistant-ui/threads→ returnsThreadSummary[]GET /api/assistant-ui/threads/:id→ used internally by the chat component
(The exact endpoints depend on how you’ve wired Assistant UI Cloud in your app, but the pattern is the same.)
Step 1: Set up state for the selected thread
In your main page or layout, maintain the currently selected thread ID in React state:
import { useState } from "react";
import { ChatPanel } from "./ChatPanel";
import { ThreadList } from "./ThreadList";
export function AssistantPage() {
const [selectedThreadId, setSelectedThreadId] = useState<string | null>(null);
return (
<div className="assistant-layout">
<ThreadList
selectedThreadId={selectedThreadId}
onSelectThread={setSelectedThreadId}
onNewThread={() => setSelectedThreadId(null)}
/>
<ChatPanel threadId={selectedThreadId} />
</div>
);
}
Here:
ThreadListis your custom UI for showing threads.ChatPanelis where you use Assistant UI’s chat components.threadId === nullmeans “start a new conversation”.
Step 2: Implement the thread list UI backed by Assistant UI Cloud
Create a ThreadList component that:
- Fetches threads from your Assistant UI Cloud–backed API.
- Renders them in a list.
- Calls
onSelectThread(threadId)when a user clicks one.
Example: basic thread list with fetch
import { useEffect, useState } from "react";
type ThreadSummary = {
id: string;
title?: string;
lastMessagePreview?: string;
updatedAt: string;
};
interface ThreadListProps {
selectedThreadId: string | null;
onSelectThread: (id: string) => void;
onNewThread: () => void;
}
export function ThreadList({
selectedThreadId,
onSelectThread,
onNewThread,
}: ThreadListProps) {
const [threads, setThreads] = useState<ThreadSummary[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function loadThreads() {
setLoading(true);
try {
const res = await fetch("/api/assistant-ui/threads");
const data: ThreadSummary[] = await res.json();
if (!cancelled) setThreads(data);
} finally {
if (!cancelled) setLoading(false);
}
}
loadThreads();
// Optional: poll or use SSE/websocket for live updates
}, []);
return (
<aside className="thread-list">
<header className="thread-list-header">
<h2>Conversations</h2>
<button onClick={onNewThread}>New chat</button>
</header>
{loading ? (
<div className="thread-list-loading">Loading…</div>
) : threads.length === 0 ? (
<div className="thread-list-empty">No conversations yet.</div>
) : (
<ul>
{threads.map((thread) => (
<li key={thread.id}>
<button
className={
thread.id === selectedThreadId
? "thread-item thread-item--active"
: "thread-item"
}
onClick={() => onSelectThread(thread.id)}
>
<div className="thread-title">
{thread.title || "Untitled conversation"}
</div>
<div className="thread-preview">
{thread.lastMessagePreview}
</div>
<time className="thread-updated-at">
{new Date(thread.updatedAt).toLocaleString()}
</time>
</button>
</li>
))}
</ul>
)}
</aside>
);
}
On the server, you implement /api/assistant-ui/threads to query Assistant UI Cloud for the user’s threads and return summaries. The exact SDK or REST call depends on your setup, but this route is the key integration point between your UI and Assistant Cloud.
Step 3: Render the chat panel with Assistant UI and thread IDs
In ChatPanel, you pass the threadId into your Assistant UI configuration. The details vary slightly depending on whether you’re using LangGraph, LangChain, or a custom LLM backend, but the pattern is:
- If
threadIdis present: load that thread’s messages from Assistant UI Cloud. - If
threadIdis null: let Assistant UI create a new thread; capture its new ID when created.
Example ChatPanel with thread support
import { useState, useEffect } from "react";
// Pseudocode imports – replace with your actual assistant-ui imports
import { AssistantUI } from "@assistant-ui/react";
interface ChatPanelProps {
threadId: string | null;
}
export function ChatPanel({ threadId }: ChatPanelProps) {
const [activeThreadId, setActiveThreadId] = useState<string | null>(threadId);
// Keep internal state in sync with parent
useEffect(() => {
setActiveThreadId(threadId);
}, [threadId]);
return (
<main className="chat-panel">
<AssistantUI
threadId={activeThreadId ?? undefined}
// Optional callback – depends on assistant-ui version/props
onThreadCreated={(newId: string) => {
setActiveThreadId(newId);
// You may also want to notify the parent via context or callback
}}
/>
</main>
);
}
If your version of Assistant UI uses a different prop (for example, a conversationId or separate configuration hook), adapt this pattern: make the chat component accept the “current thread identifier” and let the Assistant UI Cloud backend handle actual loading and streaming.
Step 4: Creating new threads
To implement a “New chat” button backed by Assistant UI Cloud:
- Set
selectedThreadIdtonullin your top-level state. - Render the chat panel without a thread ID.
- Let Assistant UI Cloud create a new thread when the user sends the first message.
- Capture the newly created
threadId(via a callback or event) and update state.
You saw the UI side in the earlier examples. On the backend, ensure your conversation backend endpoint or LangGraph workflow is configured to create a new thread in Assistant UI Cloud when no threadId is provided.
Step 5: Keeping the thread list in sync with new conversations
When a new thread is created or an existing one receives a new message, you’ll want to:
- Add it to the list if it’s new.
- Update its
updatedAtandlastMessagePreview. - Re-sort the list (e.g., most recent first).
You can implement this by:
- Refetching the thread list whenever a conversation ends or after each message.
- Or optimistic updates: update the thread list from the client-side events and periodically sync with the backend.
Example: refetch threads when activeThreadId changes:
// Inside ThreadList
useEffect(() => {
async function loadThreads() {
// same fetch as before
}
loadThreads();
}, [selectedThreadId]); // refetch when switching threads
For more advanced setups, you can integrate:
- WebSockets or Server-Sent Events from your backend
- LangGraph events with Assistant UI to push updates into your thread list
UX patterns for a thread list UI
A thread list backed by Assistant UI Cloud works best when it feels like a modern messaging app. Consider these patterns:
- Sidebar layout: thread list on the left, chat on the right (like Slack or email clients).
- Mobile-friendly: thread list on a separate view; clicking a thread navigates to
/chat/:threadId. - Distinct styling for active thread: highlight the selected thread item.
- Last message preview: show a short snippet of the last user or assistant message.
- Badges and icons: small elements like “New” or unread indicators, if your product logic supports them.
- Delete/Archive: optional actions to manage long-term thread storage (backed by delete/archive routes against Assistant UI Cloud).
Handling authentication and per-user threads
Assistant UI Cloud usually stores threads per user. Ensure:
- Your API routes (like
/api/assistant-ui/threads) are authenticated. - Each request to Assistant UI Cloud includes a user identifier (JWT, session, or API key mapping).
- The thread list only shows threads for the currently logged-in user.
This keeps threads scoped correctly and ensures users only see their own Assistant UI Cloud conversations.
Performance and scalability tips
When building a thread list UI backed by Assistant UI Cloud:
- Paginate: If users can have many threads, implement pagination or “Load more”.
- Lazy load messages: The thread list should only load thread metadata, not full chat histories.
- Streaming: Let Assistant UI handle streaming output in the chat panel; your thread list only needs final message previews once they’re ready.
- Minimal bundle size: Assistant UI is optimized for performance; keep the thread list itself simple and avoid heavy client-side computations.
Summary
To implement a thread list UI backed by Assistant UI Cloud:
- Maintain a top-level
selectedThreadIdstate. - Build a
ThreadListcomponent that fetches thread summaries from your Assistant UI Cloud–backed API. - Pass the selected
threadIdinto your Assistant UI chat component. - Let Assistant UI Cloud create new threads when no ID is provided, and capture the new ID.
- Keep the thread list in sync through refetching or real-time updates.
This pattern allows you to combine Assistant UI’s powerful, production-ready chat interface with a familiar “conversation list” experience, all backed by persistent storage in Assistant UI Cloud.