Drops APIs deprecated since v0.11/v0.12, and primitives migrate from components prop to children render functions.
Version 0.14 finishes deprecation cycles started in v0.11 and v0.12, and replaces the components prop on primitives with a children render function pattern. If your app is on v0.13 and free of deprecation warnings, the only required change is the children API migration.
Automatic Migration
The codemod that ships with the CLI handles the v0.12 hook renames (useAssistantApi → useAui, etc.) which are also removed in v0.14:
npx assistant-ui@latest upgradeThe other removals listed below are simple renames you can apply with find-and-replace.
Hook Aliases Removed
These thin aliases existed only for backwards compatibility:
| Removed | Replacement |
|---|---|
useAssistantApi | useAui |
useAssistantState | useAuiState |
useAssistantEvent | useAuiEvent |
AssistantIf | AuiIf |
useLocalThreadRuntime | useLocalRuntime |
unstable_useRemoteThreadListRuntime | useRemoteThreadListRuntime |
unstable_useCloudThreadListAdapter | useCloudThreadListAdapter |
unstable_RemoteThreadListAdapter | RemoteThreadListAdapter |
unstable_InMemoryThreadListAdapter | InMemoryThreadListAdapter |
Runtime API Cleanups
AssistantRuntime
| Removed | Replacement |
|---|---|
runtime.threadList | runtime.threads |
runtime.switchToNewThread() | runtime.threads.switchToNewThread() |
runtime.switchToThread(id) | runtime.threads.switchToThread(id) |
runtime.registerModelConfigProvider(p) | runtime.registerModelContextProvider(p) |
runtime.reset({ initialMessages }) | runtime.thread.reset(initialMessages) |
ThreadRuntime
| Removed | Replacement |
|---|---|
thread.startRun(parentId) (positional) | thread.startRun({ parentId }) |
thread.unstable_resumeRun(config) | thread.resumeRun(config) |
thread.unstable_loadExternalState(state) | thread.importExternalState(state) |
thread.getModelConfig() | thread.getModelContext() |
MessageState / MessageRuntime
The duplicate top-level submittedFeedback field has been removed. It has always been mirrored from message.metadata.submittedFeedback, which remains the source of truth:
// Before
useAuiState((s) => s.message.submittedFeedback);
// After
useAuiState((s) => s.message.metadata.submittedFeedback);ChatModelRunOptions
If you wrote a custom ChatModelAdapter, the legacy alias options.config (which mirrored options.context) is gone:
// Before
const adapter: ChatModelAdapter = {
async run({ messages, config }) {
/* config.tools, config.config, ... */
},
};
// After
const adapter: ChatModelAdapter = {
async run({ messages, context }) {
/* context.tools, context.config, ... */
},
};External Store Helpers
getExternalStoreMessage (singular) has been removed. Use getExternalStoreMessages (plural), which always returns an array — even when only one source message is bound:
// Before
const original = getExternalStoreMessage<MyMessage>(threadMessage);
// After
const [original] = getExternalStoreMessages<MyMessage>(threadMessage);Assistant Transport Helpers
toAISDKTools and getEnabledTools (re-exported from @assistant-ui/react) have been removed. Both wrapped functionality that's now exposed directly by assistant-stream:
// Before
import { toAISDKTools } from "@assistant-ui/react";
const schemas = toAISDKTools(tools);
// After
import { toToolsJSONSchema } from "assistant-stream";
const schemas = toToolsJSONSchema(tools);toToolsJSONSchema filters out disabled and backend tools by default. To replicate the old toAISDKTools behavior exactly (include all tools regardless), pass { filter: () => true }.
react-langgraph
useLangGraphRuntime's onSwitchToThread option has been removed. Use load, which has the same signature plus an optional AbortSignal:
// Before
useLangGraphRuntime({
stream,
onSwitchToThread: async (threadId) => {
return await fetchThread(threadId);
},
});
// After
useLangGraphRuntime({
stream,
load: async (threadId, { signal } = {}) => {
return await fetchThread(threadId, { signal });
},
});Children API for Primitives
Version 0.14 replaces the components prop on primitives with a children render function pattern. This gives you full control over rendering with simple inline logic.
ThreadPrimitive.Messages
Before:
<ThreadPrimitive.Messages
components={{
UserMessage,
EditComposer,
AssistantMessage,
}}
/>After:
<ThreadPrimitive.Messages>
{({ message }) => {
if (message.composer.isEditing) return <EditComposer />;
if (message.role === "user") return <UserMessage />;
return <AssistantMessage />;
}}
</ThreadPrimitive.Messages>MessagePrimitive.Parts
Before:
<MessagePrimitive.Parts
components={{
Text: MarkdownText,
Reasoning,
tools: { Fallback: ToolFallback },
}}
/>After:
<MessagePrimitive.Parts>
{({ part }) => {
if (part.type === "text") return <MarkdownText />;
if (part.type === "reasoning") return <Reasoning {...part} />;
if (part.type === "tool-call")
return part.toolUI ?? <ToolFallback {...part} />;
return null;
}}
</MessagePrimitive.Parts>part.toolUI and part.dataRendererUI
Tool-call parts now expose a toolUI property that resolves to the registered tool UI (via useAssistantToolUI / makeAssistantToolUI) or null if none is registered. Data parts similarly expose dataRendererUI.
// Renders the registered tool UI if available, otherwise ToolFallback
if (part.type === "tool-call")
return part.toolUI ?? <ToolFallback {...part} />;
// Renders the registered data renderer if available
if (part.type === "data")
return part.dataRendererUI ?? null;Returning null
Returning null from the children function renders registered tool UIs and data renderer UIs automatically via the registry. To explicitly render nothing (suppressing registered UIs), return <></>.
<MessagePrimitive.Parts>
{({ part }) => {
if (part.type === "text") return <MarkdownText />;
return null; // registered tool/data UIs still render
}}
</MessagePrimitive.Parts>ThreadPrimitive.Suggestions
Before:
<ThreadPrimitive.Suggestions
components={{ Suggestion: SuggestionItem }}
/>After:
<ThreadPrimitive.Suggestions>
{() => <SuggestionItem />}
</ThreadPrimitive.Suggestions>ThreadListPrimitive.Items
Before:
<ThreadListPrimitive.Items
components={{ ThreadListItem: MyThreadListItem }}
/>After:
<ThreadListPrimitive.Items>
{() => <MyThreadListItem />}
</ThreadListPrimitive.Items>Attachments
Before:
<ComposerPrimitive.Attachments
components={{ Attachment: MyAttachment }}
/>After:
<ComposerPrimitive.Attachments>
{() => <MyAttachment />}
</ComposerPrimitive.Attachments>addResult / resume on enriched part state
When using the children API, tool-call parts include addResult and resume methods directly on the part object. This means <ToolFallback {...part} /> works without needing a wrapper component to provide these methods.
Backwards Compatibility
The components prop is still supported on all primitives but is deprecated and will be removed in a future version.
Still Deprecated (not removed)
Some APIs marked @deprecated are intentionally kept for one more release to give downstream runtimes time to migrate:
ThreadState.threadIdandThreadState.metadata— useuseThreadListItem()(see v0.12 guide). Will be removed in a future minor release.- Legacy context hooks under
legacy-runtime/hooks/*(useThreadRuntime,useMessageRuntime, etc.) — replaced byuseAui()/useAuiState()in v0.12. The codemod migrates these. - Primitive
Ifcomponents (ThreadIf,MessageIf,ComposerIf,ThreadEmpty) — replaced byAuiIf. The codemod migrates these.
You'll continue to see deprecation warnings on these; expect removal in a future minor release.
Getting Help
- File issues at https://github.com/assistant-ui/assistant-ui/issues
- See the deprecation policy for the support window