Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/create-chat-hook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/ai-react': minor
'@tanstack/ai-solid': minor
'@tanstack/ai-vue': minor
'@tanstack/ai-svelte': minor
---

Chat UI now lives on the framework packages at `@tanstack/ai-react/ui`, `@tanstack/ai-solid/ui`, `@tanstack/ai-vue/ui`, and `@tanstack/ai-svelte/ui`.

Call `useChat(options)` from the framework package. Pass that instance and a typed `components` map to `<Chat chat={chat} components={components} />`. `useChatContext()` reads the same instance from inside `Chat`.

`createChatHook(options)` on the main package still returns a bound `useChat`. It does not take `chatComponents`.
11 changes: 11 additions & 0 deletions .changeset/deprecate-ai-ui-packages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@tanstack/ai-react-ui': minor
'@tanstack/ai-solid-ui': minor
'@tanstack/ai-vue-ui': minor
---

Deprecated. Import chat UI from `@tanstack/ai-react/ui`, `@tanstack/ai-solid/ui`, or `@tanstack/ai-vue/ui` instead.

These packages re-export the new `/ui` subpath until 1.0.0. `npm install` prints a deprecation warning.

`@tanstack/ai-svelte-ui` was never published. Svelte chat UI lives only on `@tanstack/ai-svelte/ui`.
8 changes: 4 additions & 4 deletions .changeset/typed-headless-chat-ui.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
'@tanstack/ai-client': minor
'@tanstack/ai-react-ui': minor
'@tanstack/ai-solid-ui': minor
'@tanstack/ai-vue-ui': minor
'@tanstack/ai-svelte-ui': minor
'@tanstack/ai-react': minor
'@tanstack/ai-solid': minor
'@tanstack/ai-vue': minor
'@tanstack/ai-svelte': minor
---

Add typed headless `createChatUI()` adapters. Chat options control the types of message parts, tools, structured output, and interrupts. `defineComponents` requires a component for every tool name and every registered interrupt id. `InterruptProps` pins a tool approval or a registered generic interrupt. Old Chat orchestration stays importable and deprecated until 1.0.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,15 @@ on.

## Framework Packages

| Package | What it provides |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client) | Headless chat, realtime, and generation clients |
| [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react) | React hooks including `useChat`, `useRealtimeChat`, and generation hooks |
| [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid) | Solid hooks for chat and generations |
| [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue) | Vue composables for chat and generations |
| [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte) | Svelte 5 factories for chat and generations |
| [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact) | Preact hooks for chat |
| `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, `@tanstack/ai-vue-ui` | Headless UI components for chat interfaces |
| Package | What it provides |
| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client) | Headless chat, realtime, and generation clients |
| [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react) | React hooks including `useChat`, `useRealtimeChat`, and generation hooks |
| [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid) | Solid hooks for chat and generations |
| [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue) | Vue composables for chat and generations |
| [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte) | Svelte 5 factories for chat and generations |
| [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact) | Preact hooks for chat |
| `@tanstack/ai-react/ui`, `@tanstack/ai-solid/ui`, `@tanstack/ai-vue/ui`, `@tanstack/ai-svelte/ui` | Headless UI components for chat interfaces. `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, and `@tanstack/ai-vue-ui` are deprecated. |

## Advanced Docs

Expand Down
6 changes: 3 additions & 3 deletions codemods/ag-ui-compliance/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ import type {

const FRAMEWORK_USE_CHAT_PACKAGES = new Set([
'@tanstack/ai-react',
'@tanstack/ai-react-ui',
'@tanstack/ai-react/ui',
'@tanstack/ai-vue',
'@tanstack/ai-vue-ui',
'@tanstack/ai-vue/ui',
'@tanstack/ai-solid',
'@tanstack/ai-solid-ui',
'@tanstack/ai-solid/ui',
'@tanstack/ai-preact',
])

Expand Down
23 changes: 22 additions & 1 deletion docs/api/ai-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ keywords:

React hooks for TanStack AI, providing convenient React bindings for the headless client.

For a typed headless chat UI, see [React Chat UI](../ui/react) and [Migrate to createChatUI](../migration/create-ui).
For a typed headless chat UI, see [React Chat UI](../ui/react) and [Chat UI packages](../migration/create-ui).
For React Native, the documented support surface is narrow: `useChat` with chat
connection adapters. React DOM-specific UI packages and TanStack AI devtools UI
are not part of the React Native support surface.
Expand All @@ -28,6 +28,27 @@ For a complete native journey, see
npm install @tanstack/ai-react
```

## `createChatHook(options)`

Bind `chatOptions` once at module scope. Call `useChat()` in the screen to create the instance. Per-call overrides may set `threadId`, `initialMessages`, `live`, and `forwardedProps`. They must not change `tools`, `interrupts`, or `outputSchema`.

```tsx
import { createChatHook, fetchServerSentEvents } from "@tanstack/ai-react";

const chatOptions = {
connection: fetchServerSentEvents("/api/chat"),
};

const { useChat } = createChatHook(chatOptions);

function ChatScreen() {
const chat = useChat({ threadId: "support-1" });
return null;
}
```

`useChat(chatOptions)` from this package still works when you want to pass the full object at the call site. Rename the bound hook if both are in one file: `const { useChat: useSupportChat } = createChatHook(chatOptions)`.

## `useChat(options?)`

Main hook for managing chat state in React with full type safety.
Expand Down
23 changes: 22 additions & 1 deletion docs/api/ai-solid.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,35 @@ keywords:

SolidJS primitives for TanStack AI, providing convenient SolidJS bindings for the headless client.

For a typed headless chat UI, see [Solid Chat UI](../ui/solid) and [Migrate to createChatUI](../migration/create-ui).
For a typed headless chat UI, see [Solid Chat UI](../ui/solid) and [Chat UI packages](../migration/create-ui).

## Installation

```bash
npm install @tanstack/ai-solid
```

## `createChatHook(options)`

Bind `chatOptions` once at module scope. Call `useChat()` in the screen to create the instance. Per-call overrides may set `threadId`, `initialMessages`, `live`, and `forwardedProps`. They must not change `tools`, `interrupts`, or `outputSchema`.

```tsx
import { createChatHook, fetchServerSentEvents } from "@tanstack/ai-solid";

const chatOptions = {
connection: fetchServerSentEvents("/api/chat"),
};

const { useChat } = createChatHook(chatOptions);

function ChatScreen() {
const chat = useChat({ threadId: "support-1" });
return null;
}
```

`useChat(chatOptions)` from this package still works when you want to pass the full object at the call site. Rename the bound primitive if both are in one file: `const { useChat: useSupportChat } = createChatHook(chatOptions)`.

## `useChat(options?)`

Main primitive for managing chat state in SolidJS with full type safety.
Expand Down
19 changes: 18 additions & 1 deletion docs/api/ai-svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,31 @@ keywords:

Svelte 5 bindings for TanStack AI, providing reactive factory functions for the headless client using Svelte runes.

For a typed headless chat UI, see [Svelte Chat UI](../ui/svelte) and [Migrate to createChatUI](../migration/create-ui).
For a typed headless chat UI, see [Svelte Chat UI](../ui/svelte) and [Chat UI packages](../migration/create-ui).

## Installation

```bash
npm install @tanstack/ai-svelte
```

## `createChatHook(options)`

Bind `chatOptions` once at module scope. Call `createChat()` to create the instance. Per-call overrides may set `threadId`, `initialMessages`, `live`, and `forwardedProps`. They must not change `tools`, `interrupts`, or `outputSchema`.

```ts
import { createChatHook, fetchServerSentEvents } from "@tanstack/ai-svelte";

const chatOptions = {
connection: fetchServerSentEvents("/api/chat"),
};

const { createChat } = createChatHook(chatOptions);
const chat = createChat({ threadId: "support-1" });
```

`createChat(chatOptions)` from this package still works when you want to pass the full object at the call site. Rename the bound factory if both are in one file: `const { createChat: createSupportChat } = createChatHook(chatOptions)`.

## `createChat(options)`

Factory function for managing chat state in Svelte 5 with full type safety.
Expand Down
20 changes: 19 additions & 1 deletion docs/api/ai-vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,32 @@ keywords:

Vue composables for TanStack AI, providing convenient Vue 3 bindings for the headless client.

For a typed headless chat UI, see [Vue Chat UI](../ui/vue) and [Migrate to createChatUI](../migration/create-ui).
For a typed headless chat UI, see [Vue Chat UI](../ui/vue) and [Chat UI packages](../migration/create-ui).

## Installation

```bash
npm install @tanstack/ai-vue
```

## `createChatHook(options)`

Bind `chatOptions` once at module scope. Call `useChat()` in the screen to create the instance. Per-call overrides may set `threadId`, `initialMessages`, `live`, and `forwardedProps`. They must not change `tools`, `interrupts`, or `outputSchema`.

```ts
import { createChatHook, fetchServerSentEvents } from "@tanstack/ai-vue";

const chatOptions = {
connection: fetchServerSentEvents("/api/chat"),
};

const { useChat } = createChatHook(chatOptions);

const chat = useChat({ threadId: "support-1" });
```

`useChat(chatOptions)` from this package still works when you want to pass the full object at the call site. Rename the bound composable if both are in one file: `const { useChat: useSupportChat } = createChatHook(chatOptions)`.

## `useChat(options?)`

Main composable for managing chat state in Vue with full type safety.
Expand Down
14 changes: 7 additions & 7 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,31 +219,31 @@
"label": "React",
"to": "ui/react",
"addedAt": "2026-08-26",
"updatedAt": "2026-08-27"
"updatedAt": "2026-08-30"
},
{
"label": "Solid",
"to": "ui/solid",
"addedAt": "2026-08-26",
"updatedAt": "2026-08-27"
"updatedAt": "2026-08-30"
},
{
"label": "Vue",
"to": "ui/vue",
"addedAt": "2026-08-26",
"updatedAt": "2026-08-27"
"updatedAt": "2026-08-30"
},
{
"label": "Svelte",
"to": "ui/svelte",
"addedAt": "2026-08-26",
"updatedAt": "2026-08-27"
"updatedAt": "2026-08-30"
},
{
"label": "Custom Adapters",
"to": "ui/custom-adapters",
"addedAt": "2026-08-26",
"updatedAt": "2026-08-26"
"updatedAt": "2026-08-30"
}
]
},
Expand Down Expand Up @@ -886,10 +886,10 @@
"updatedAt": "2026-07-22"
},
{
"label": "createChatUI",
"label": "Chat UI packages",
"to": "migration/create-ui",
"addedAt": "2026-08-26",
"updatedAt": "2026-08-27"
"updatedAt": "2026-08-30"
},
{
"label": "From Vercel AI SDK",
Expand Down
68 changes: 22 additions & 46 deletions docs/migration/create-ui.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,35 @@
---
title: Migrate to createChatUI
title: Chat UI packages
id: migrate-create-ui
order: 5
description: "Move chat-state ownership out of the old Chat component and onto createChatUI with a typed component map."
description: "Move from @tanstack/ai-*-ui to the framework /ui subpath. Call useChat and render Chat with a component map."
keywords:
- tanstack ai
- createChatUI
- Chat
- migration
- deprecation
---

The old `Chat` component owned chat state and lost configured types. `createChatUI` keeps types from your `chatOptions` and leaves `useChat` in your app.
Change your import from `@tanstack/ai-react-ui` to `@tanstack/ai-react/ui`. Call `useChat(chatOptions)` and render `<Chat chat={chat} components={components} />`.

This is a semantic migration. There is no codemod.
The same move applies to Solid, Vue, and Svelte.

> **Deprecated.** `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, and `@tanstack/ai-vue-ui` re-export the new `/ui` subpath until each package's `1.0.0`. `npm install` prints a warning. Import from `/ui` in new code. Svelte never published a `*-ui` package. Use `@tanstack/ai-svelte/ui`.

## What changes

1. You call `useChat` or `createChat` yourself.
2. You supply every visible component.
3. Tool inputs stay optional while they stream.
4. Tool approvals come from `chat.interrupts`.
5. Unknown runtime keys can use a fallback or render nothing.
6. `createChatUI()` must run at module scope so identity stays stable.
1. Chat UI lives on the framework package: `@tanstack/ai-react/ui`, `@tanstack/ai-solid/ui`, `@tanstack/ai-vue/ui`, `@tanstack/ai-svelte/ui`.
2. You call `useChat` or `createChat` yourself.
3. You pass a `components` map into `Chat`.
4. You supply every visible component. There is no default markup.

## Why

The old APIs drop configured types, keep unused properties, use a deprecated approval path, cover only part of the message protocol, and own chat state. Two orchestration models duplicate fixes.

## Minimum versions

- `@tanstack/ai-react-ui` 0.9.0
- `@tanstack/ai-solid-ui` 0.8.0
- `@tanstack/ai-vue-ui` 0.3.0
- `@tanstack/ai-svelte-ui` 0.2.0

Old orchestration exports stay importable until each package's `1.0.0`. `TextPart` and `ThinkingPart` stay supported.
A separate `*-ui` package split chat UI from the framework package. The old `Chat` component also owned chat state and dropped configured types. `Chat` from `/ui` keeps types from your `chatOptions` and leaves state on `useChat`.

## Before

```tsx
```tsx ignore
import { fetchServerSentEvents } from '@tanstack/ai-react'
import { Chat, ChatMessages, ChatInput } from '@tanstack/ai-react-ui'

Expand All @@ -58,25 +49,21 @@ export function OldChat() {

```tsx
import { fetchServerSentEvents, useChat } from '@tanstack/ai-react'
import { createChatUI } from '@tanstack/ai-react-ui'
import { Chat, type ChatUIComponents } from '@tanstack/ai-react/ui'

const chatOptions = {
connection: fetchServerSentEvents('/api/chat'),
}

const UI = createChatUI(chatOptions)

const components = UI.defineComponents({
const components = {
layout: ({ renderMessages, renderInput }) => (
<main>
{renderMessages()}
{renderInput()}
</main>
),
message: ({ renderParts }) => <article>{renderParts()}</article>,
input: () => {
const chat = UI.useChat()
return (
input: ({ chat }) => (
<form
onSubmit={(event) => {
event.preventDefault()
Expand All @@ -90,32 +77,21 @@ const components = UI.defineComponents({
>
<input name="message" />
</form>
)
},
),
parts: { fallback: () => null },
})
} satisfies ChatUIComponents<typeof chatOptions>

export function NewChat() {
const chat = useChat(chatOptions)
return <UI.Chat chat={chat} components={components} />
return <Chat chat={chat} components={components} />
}
```

## Steps

1. Move `connection`, `tools`, and `interrupts` into a module-level `chatOptions` object.
2. Call `createChatUI(chatOptions)` next to that object.
2. Build a `components` map typed with `ChatUIComponents<typeof chatOptions>`.
3. Call `useChat(chatOptions)` in the screen component.
4. Define `layout`, `message`, `parts`, `tools`, and `interrupts` in `defineComponents`.
5. Replace `<Chat>` with `<UI.Chat chat={chat} components={components} />`.

## Gotchas

- A shared `chatOptions` variable does not need `as const`.
- A mapped tool can read `interrupt` and render the approval itself. That approval stays off the list. A component on `interrupts.tools` uses the list.
- Generic interrupts live under `interrupts.generic`: a registered id such as `choosePlan`, plus `fallback`. Unbound interrupts use `fallback`.
- TypeScript requires a `tools` component for every tool name and an `interrupts.generic` component for every interrupt id. `generic.fallback` is optional.
- Matched `tool-result` parts are hidden in automatic traversal. Unmatched results stay visible.
- Nested providers use the nearest chat instance.
4. Render `<Chat chat={chat} components={components} />`.

See the [React UI guide](../ui/react) for a full map.
Old orchestration exports stay importable from the deprecated `*-ui` packages until `1.0.0`.
Loading
Loading