Skip to content
API

@tanstack/ai-svelte

Svelte 5 factory functions for TanStack AI, built on runes. To build your first chat, start with the Quick Start. For a typed chat layout, see Svelte Chat UI.

Installation

shell
npm i @tanstack/ai-svelte

The factories are functions, not hooks, so their names start with create. Call them during component init.

createChat(options)

Holds the messages and the run state of one chat, and talks to your server route.

svelte
<script lang="ts">
  import { createChat, fetchServerSentEvents } from "@tanstack/ai-svelte";

  let input = $state("");
  const chat = createChat({
    connection: fetchServerSentEvents("/api/chat"),
    threadId: "support-chat",
  });

  const handleSubmit = (e: Event) => {
    e.preventDefault();
    if (input.trim() && !chat.isLoading) {
      chat.sendMessage(input);
      input = "";
    }
  };
</script>

{#each chat.messages as message (message.id)}
  <div>
    <strong>{message.role}:</strong>
    {#each message.parts as part}
      {#if part.type === "text"}<span>{part.content}</span>{/if}
    {/each}
  </div>
{/each}
<form onsubmit={handleSubmit}>
  <input bind:value={input} />
  <button type="submit" disabled={chat.isLoading}>Send</button>
</form>

Options

You must pass threadId. You must also pass connection or fetcher, not both.

OptionWhat it does
connectionConnection adapter, for example fetchServerSentEvents("/api/chat"). See Transports.
fetcherA function that returns a Response or a stream. Use it with server functions.
toolsClient tools from .client(). They run when the model calls them. Read reactive state in a get tools() getter to change them later. See Client Tools.
threadIdRequired. The id of this chat. Use a stable id for each conversation, for example the id from your route.
initialMessagesThe messages to show first.
forwardedPropsJSON sent to the server in the AG-UI forwardedProps field, for example { model: "gpt-6.1-sol" }.
persistencetrue to load the thread from the server, or a storage adapter. See Chat Persistence.
outputSchemaA Standard Schema. Adds typed partial and final to the return value. See Streaming structured output.
contextClient-only runtime context for client tools. It is not sent to the server.
liveSubscribe when the chat is created.
queueWhat sendMessage does while a run is busy. Default: queue the message. See Queueing.
interruptsDefinitions from defineInterrupt(). See Interrupts.
byok, byokProviderA keyring from defineByok(). See Bring Your Own Key.
devtoolsDisplay options for TanStack AI Devtools.

The factory also takes the ChatClient callbacks (onResponse, onChunk, onFinish, onError, onInterruptStateChange, onCustomEvent) and the other client options. See ChatClient options.

To change forwardedProps or context after the chat starts, call chat.updateForwardedProps(value) or chat.updateContext(value).

Returns

State fields are reactive getters backed by $state. Read them on the object, for example chat.messages. Do not destructure them, or you keep only the first value.

FieldWhat it is
messagesThe UIMessage array of the chat.
sendMessage(content, options?)Sends a user message. content is a string or { content, id?, metadata? }. options takes whenBusy and body.
isLoadingtrue while a response streams.
errorThe last error, or undefined.
status"ready", "submitted", "streaming", or "error".
stop()Stops the current response.
reload()Runs the last user message again.
append(message)Adds a UIMessage or ModelMessage and starts a run.
addToolResult(result)Gives the result of a client tool by hand: { toolCallId, tool, output, state?, errorText? }.
setMessages(messages), clear()Replace or clear the messages.
queue, cancelQueued(id)Messages that wait while a run is busy. See Queueing.
runIdThe id of the run in flight, or null.
interruptsPending interrupts. Each item has resolveInterrupt(...). See Interrupts.
resolveInterrupts, cancelInterrupts, retryInterrupts, resumeInterruptsUnsafeAct on all pending interrupts at once.
interruptErrors, resumingErrors from the last resume, and true while a resume runs.
isHydratingtrue while the chat loads from persistence.
hasOlderMessages, loadOlderMessages()Page in older messages. See Chat Persistence.
isSubscribed, connectionStatus, sessionGeneratingState of the live subscription.
partial, finalStructured output. Only when you pass outputSchema.
updateForwardedProps(value), updateContext(value)Change forwardedProps or context for the next sends.
dispose()Ends the chat client and releases devtools.

When the component is destroyed, the chat drops its connection and keeps its messages. When you are done with the chat, call dispose() yourself.

Example: tool approval

A tool with needsApproval: true pauses the run. The pending call shows up in interrupts.

svelte
<script lang="ts">
  import { toolDefinition } from "@tanstack/ai";
  import { createChat, fetchServerSentEvents } from "@tanstack/ai-svelte";
  import { z } from "zod";

  const deleteFileTool = toolDefinition({
    name: "delete_file",
    description: "Delete a file",
    needsApproval: true,
    inputSchema: z.object({ path: z.string() }),
  });

  const chat = createChat({
    connection: fetchServerSentEvents("/api/chat"),
    threadId: "support-chat",
    tools: [deleteFileTool],
  });
</script>

{#each chat.interrupts as interrupt (interrupt.id)}
  {#if interrupt.kind === "tool-approval"}
    <div>
      <p>Approve: {interrupt.toolName}</p>
      <button onclick={() => interrupt.resolveInterrupt(true)}>Approve</button>
      <button onclick={() => interrupt.resolveInterrupt(false)}>Deny</button>
    </div>
  {/if}
{/each}

See Tool Approval for the server side.

createChatHook(options)

Binds chat options once at module scope and returns a createChat factory that uses them.

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

export const { createChat: createSupportChat } = createChatHook({
  connection: fetchServerSentEvents("/api/chat"),
});

// In a component: const chat = createSupportChat({ threadId: "support-1" })

The bound factory takes these per-call overrides: threadId, initialMessages, live, and forwardedProps. Keep tools, interrupts, and outputSchema on the factory options.

@tanstack/ai-svelte/ui has its own createChatHook that also returns typed UI components. See Svelte Chat UI.

Generation hooks

Each factory runs one generation request and keeps its result. See Generation Hooks.

Factorygenerate() inputresult
createGenerateImageImageGenerateInputImageGenerationResult
createGenerateAudioAudioGenerateInputAudioGenerationResult
createGenerateSpeechSpeechGenerateInputTTSResult
createTranscriptionTranscriptionGenerateInputTranscriptionResult
createSummarizeSummarizeGenerateInputSummarizationResult
createGenerateVideoVideoGenerateInputVideoGenerateResult
createGenerationYour own input typeYour own result type
svelte
<script lang="ts">
  import { createGenerateImage, fetchServerSentEvents } from "@tanstack/ai-svelte";

  const image = createGenerateImage({
    connection: fetchServerSentEvents("/api/generate/image"),
    threadId: "image-studio",
  });
</script>

<button
  disabled={image.isLoading}
  onclick={() => image.generate({ prompt: "A mountain at sunset" })}
>
  Generate
</button>

Options: threadId (required), connection or fetcher, body, persistence, byok, byokProvider, devtools, onResult, onError, onProgress, onChunk.

Returns: generate(input), result, isLoading, error, status, stop(), reset(), runId, updateBody(body), and dispose(). The state fields are reactive getters. The factories do not clean up on their own. When you are done, call dispose().

createGenerateVideo also returns jobId and videoStatus, and takes onJobCreated and onStatusUpdate.

Realtime chat

This package does not export a realtime factory. Use RealtimeClient, which this package re-exports. See Realtime Chat.

createAudioRecorder(options?)

Records audio from the microphone. See Audio Recording.

Returns recording, isRecording, isSupported (reactive getters), start(), stop(), and cancel(). Pass onComplete to transform the recording. Then recording and stop() use the transformed type.

createByok(client)

Subscribes to a BYOK keyring. The return value has a reactive snapshot getter. See Bring Your Own Key.

svelte
<script lang="ts">
  import { createByok } from "@tanstack/ai-svelte";
  import { byok } from "./byok";

  const byokState = createByok(byok);
  const openai = $derived(byokState.snapshot.status.openai);
</script>

<p>{openai && "masked" in openai ? openai.masked : "No key"}</p>

Keep the object and read byokState.snapshot in markup or $derived. If you destructure snapshot, you keep only the first value. The snapshot has status, locked, prompt, and storageError. Save a key from your own UI with byok.update(provider, value).

MCP App bridge

This package does not export a bridge factory. Use createMcpAppBridge, which this package re-exports. See MCP Apps.

createRegisterWebMCPTools(tools, options?)

Exposes client tools to the browser WebMCP API. Call it during component init. Svelte removes the tools when the component is destroyed. See WebMCP Tools.

svelte
<script lang="ts">
  import { createRegisterWebMCPTools } from "@tanstack/ai-svelte";
  import { searchProducts } from "./tools";

  createRegisterWebMCPTools([searchProducts], {
    onError: (error) => console.error(error),
  });
</script>

Options: toolOptions, context, and onError. context is required when a tool declares a required runtime context.

createPageWebMCPTools(options?)

Reads the WebMCP tools that the page registered, as client tools. The tools field starts empty and updates when the page adds or removes a tool. Read it in a get tools() getter on createChat. See Page WebMCP Tools in Chat.

svelte
<script lang="ts">
  import { createPageWebMCPTools } from "@tanstack/ai-svelte";

  const page = createPageWebMCPTools({
    filter: (tool) => tool.origin === location.origin,
  });
</script>

<p>{page.tools.length} page tools</p>

Options: filter (return false to skip a tool) and onError.

Re-exports from @tanstack/ai-client

You can import these from @tanstack/ai-svelte directly. See @tanstack/ai-client for details.

GroupExports
Connection adaptersfetchServerSentEvents, fetchHttpStream, fetchJson, xhrServerSentEvents, xhrHttpStream, webSocket, stream, rpcStream
Typed optionscreateChatClientOptions, clientTools
PersistencelocalStoragePersistence, sessionStoragePersistence, indexedDBPersistence
ClientsChatClient, GenerationClient, VideoGenerationClient, RealtimeClient, AudioRecorder, InterruptManager
WebMCP and MCP AppsregisterWebMCPTools, getWebMCPTools, subscribeWebMCPTools, createMcpAppBridge
ErrorsUnsupportedResponseStreamError, StreamTruncatedError, DurableStreamIncompleteError, StreamReconnectLimitError, StorageUnavailableError
Stream helpersStreamProcessor, chunk strategies, parsePartialJSON, message converters

BYOK helpers (defineByok, defaultByokStorage, and more) are on the @tanstack/ai-svelte/byok subpath.

Types

TypeWhat it is
CreateChatOptions, CreateChatReturnThe options and return value of createChat.
UIMessage<TTools>A chat message with typed parts.
InferChatMessages<T>The message type for a createChatClientOptions result.
SendMessageOptions, WhenBusy, QueueOption, QueuedMessageSend and queue types.
DeepPartial<T>The type of partial while structured output streams.
Create<Factory>Options, Create<Factory>ReturnOptions and return types for the generation and recorder factories. The WebMCP factories export only an options type.

Pass a plain tools array to createChat or createChatClientOptions. Tool names, inputs, and outputs stay typed without as const. Import toolDefinition from @tanstack/ai.