Threads

Threads

Overview

To navigate the list of threads and view them, you must pass the required props: threads and thread.

threads – an array of objects. You can find the object structure on the API – Threads page.

thread – one of the objects from the threads array.

If your thread structure doesn’t match CUI Kit, you can use an Adapter with ready-made solutions for popular models.

Example thread structure

{
  "id": "test-thread",
  "title": "Second thread",
  "date": "2025-01-18T12:00:00.000Z",
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "Hello!"
      }
    },
    {
      "role": "assistant",
      "content": "Hello there! How can I assist you today?"
    }
  ]
}

Message content

The structure of message content differs depending on the role value. For role='user':

type UserContent = string | ({
  type: "image_url",
  image_url: { url: string }
} | {
  type: "text",
  text: string
})[];

For role='assistant':

type AssistantContent = string | ({
    type: "text",
    text: string
})[];

Thread actions

You can manage threads using the API reference, or pass your own callback functions that will be called after the user interacts with a thread.

For the latest list of callback functions, see the API – Threads page.

This example demonstrates how callback functions operate during thread interactions:

Collapse code
Expand code
<ChatPage
  initialThread={threads[0]}
  threads={threads}
  handleStopMessageStreaming={handleStopMessageStreaming}
  onUserMessageSent={onUserMessageSent}
  onFirstMessageSent={onFirstMessageSent}
  onThreadDeleted={onThreadDeleted}
  onChangeCurrentThread={onChangeCurrentThread}
  handleCreateNewThread={handleCreateNewThread}
/>
import * as React from "react";
import {
  ChatPage,
  useAssistantAnswerMock,
  Thread,
} from "@plteam/chat-ui";
import Box from "@mui/material/Box";
import Snackbar, { SnackbarCloseReason } from "@mui/material/Snackbar";
import IconButton from "@mui/material/IconButton";
import CloseIcon from "@mui/icons-material/Close";

let counter = 0;

const App: React.FC = () => {
  const [open, setOpen] = React.useState(false);
  const [text, setText] = React.useState('');

  const openSnackbar = (text: string) => {
    setText(text);
    setOpen(true);
  };

  const handleClose = (_event: React.SyntheticEvent | Event, reason?: SnackbarCloseReason) => {
    if (reason === 'clickaway') {
      return;
    }
    setOpen(false);
  };

  const [threads] = React.useState<Thread[]>([
    {
      id: "test-thread",
      title: "Second thread",
      date: (new Date('2025-01-18T12:00:00.000Z')).toISOString(),
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: "Hello!",
          },
        },
        {
          role: "assistant",
          content: "Hello there! How can I assist you today?",
        },
      ],
    },
    {
      id: "test-thread2",
      title: "First thread",
      date: (new Date('2024-12-12T12:00:00.000Z')).toISOString(),
      messages: [
        {
          role: "user",
          content: "Hello, how are you today?",
        },
        {
          role: "assistant",
          content: "Hi there! I'm doing great, thanks for asking. What can I help you with this morning?",
        },
      ],
    },
  ]);

  const onFirstMessageSent = ({ thread }: { thread: Thread }) => {
    openSnackbar(`The first message in thread "${thread.title}" has been sent`);
  }

  const onThreadDeleted = ({ thread }: { thread: Thread }) => {
    openSnackbar(`Thread deleted: ${thread.title}`);
  }

  const onChangeCurrentThread = ({ thread }: { thread: Thread }) => {
    openSnackbar(`Current thread changed: ${thread.title}`);
  }

  const handleCreateNewThread = (): Thread => {
    openSnackbar('Opened a new thread with our data');
    counter++;
    return ({
      id: `thread${counter}`,
      title: `Thread #${counter}`,
      messages: [],
      date: new Date().toISOString(),
    });
  }

  const snackBarActions = React.useMemo(() => (
    <IconButton
      size="small"
      aria-label="close"
      color="inherit"
      onClick={handleClose}
    >
      <CloseIcon fontSize="small" />
    </IconButton>
  ), [handleClose]);


  const { onUserMessageSent, handleStopMessageStreaming } =
    useAssistantAnswerMock();

  return (
    <>
      <Box height={"100dvh"} width={"100dvw"}>
        <ChatPage
          initialThread={threads[0]}
          threads={threads}
          handleStopMessageStreaming={handleStopMessageStreaming}
          onUserMessageSent={onUserMessageSent}
          onFirstMessageSent={onFirstMessageSent}
          onThreadDeleted={onThreadDeleted}
          onChangeCurrentThread={onChangeCurrentThread}
          handleCreateNewThread={handleCreateNewThread}
        />
      </Box>
      <Snackbar
        open={open}
        onClose={handleClose}
        message={text}
        action={snackBarActions}
      />
    </>
  );
}

export default App;

Custom actions

This functionality is currently under development.