@openuidev/react-headless

API reference for chat state, hooks, streaming adapters, and message types.

Use this package when you want headless chat state + streaming, with or without prebuilt UI.

Import

import {
  ChatProvider,
  fetchLLM,
  restStorage,
  useThread,
  useThreadList,
  openAIAdapter,
  openAIResponsesAdapter,
  openAIReadableStreamAdapter,
  vercelAIAdapter,
  agUIAdapter,
  openAIMessageFormat,
  openAIConversationMessageFormat,
  vercelAIMessageFormat,
  identityMessageFormat,
  processStreamedMessage,
  MessageProvider,
  useMessage,
  EventType,
  type ArtifactCategory,
  type ArtifactRendererConfig,
  type ChatLLM,
  type ChatStorage,
} from "@openuidev/react-headless";

ChatProvider

Provides chat, thread, streaming, and optional artifact state to its children.

interface ChatProviderProps {
  llm: ChatLLM;
  storage?: ChatStorage;
  artifactRenderers?: ReadonlyArray<ArtifactRendererConfig>;
  artifactCategories?: ArtifactCategory[];
  artifactAutoOpen?: boolean;
  children: React.ReactNode;
}

llm is required. It sends messages to your backend and specifies the adapter used to parse the streaming response. Create one with fetchLLM() or implement the ChatLLM interface yourself.

const llm = fetchLLM({
  url: "/api/chat",
  streamAdapter: agUIAdapter(),
});

function App() {
  return (
    <ChatProvider llm={llm}>
      <YourChatUI />
    </ChatProvider>
  );
}

storage is optional. Without it, conversations are stored in memory and are cleared when the page reloads. Use restStorage() for the built-in REST conventions, or provide your own ChatStorage implementation:

const storage = restStorage({ baseUrl: "/api/threads" });

<ChatProvider llm={llm} storage={storage}>
  <YourChatUI />
</ChatProvider>;

useThread()

Thread-level state/actions used throughout chat docs.

function useThread(): ThreadState & ThreadActions;
function useThread<T>(selector: (state: ThreadState & ThreadActions) => T): T;

Shape:

type ThreadState = {
  messages: Message[];
  isRunning: boolean;
  isLoadingMessages: boolean;
  threadError: Error | null;
};

type ThreadActions = {
  processMessage: (message: CreateMessage) => Promise<void>;
  appendMessages: (...messages: Message[]) => void;
  updateMessage: (message: Message) => void;
  setMessages: (messages: Message[]) => void;
  deleteMessage: (messageId: string) => void;
  cancelMessage: () => void;
};

useThreadList()

Thread list state/actions for sidebars and history.

function useThreadList(): ThreadListState & ThreadListActions;
function useThreadList<T>(selector: (state: ThreadListState & ThreadListActions) => T): T;

useMessage()

Access the current message inside a message component.

function useMessage(): { message: Message };

Provided via MessageProvider / MessageContext.

Stream adapters

Adapters referenced in integration guides:

function openAIAdapter(): StreamProtocolAdapter; // OpenAI Chat Completions stream
function openAIResponsesAdapter(): StreamProtocolAdapter; // OpenAI Responses stream
function openAIReadableStreamAdapter(): StreamProtocolAdapter; // OpenAI ReadableStream
function vercelAIAdapter(): StreamProtocolAdapter; // Vercel AI SDK v6 UIMessage stream
function agUIAdapter(): StreamProtocolAdapter; // AG-UI protocol stream

Related type:

interface StreamProtocolAdapter {
  parse(response: Response): AsyncIterable<AGUIEvent>;
}

Message format adapters

Converters referenced in integration guides:

const openAIMessageFormat: MessageFormat; // Chat Completions format
const openAIConversationMessageFormat: MessageFormat; // Responses/Conversations item format
const vercelAIMessageFormat: MessageFormat; // Vercel AI SDK v6 UIMessage format
const identityMessageFormat: MessageFormat; // Pass-through (no conversion)

Base type:

interface MessageFormat {
  toApi(messages: Message[]): unknown;
  fromApi(data: unknown): Message[];
}

The Vercel AI SDK integration supports app-executed tools. Provider-executed tools (providerExecuted: true) are rejected because AG-UI messages cannot preserve their assistant-contained result semantics.

Message types

type Message =
  | UserMessage
  | AssistantMessage
  | SystemMessage
  | DeveloperMessage
  | ToolMessage
  | ActivityMessage
  | ReasoningMessage;

Key message shapes:

interface UserMessage {
  role: "user";
  id: string;
  content: InputContent[];
}

interface AssistantMessage {
  role: "assistant";
  id: string;
  content: string | null;
  toolCalls?: ToolCall[];
}

Streaming utilities

function processStreamedMessage(/* ... */): Promise<void>;

Low-level utility for processing a streamed response outside of ChatProvider.

On this page