@langchain/svelte v0 to v1@langchain/svelte v1 targets new event-based streaming. The useStream import stays the same, but the option bag, return shape, and how you subscribe to scoped data all change. Most chat apps migrate in well under an hour.
The v0 binding was built on the legacy streaming protocol and accreted a large surface of opt-in callbacks (onUpdateEvent, onCustomEvent, onDebugEvent, onCheckpointEvent, …) and derived state (history, branch, experimental_branchTree, activeSubagents, subagents) that had to be recomputed on every update — whether or not any component was rendering it.
v1 flips that around:
values / messages / toolCalls / interrupts are always reactive at the root with no extra subscription cost beyond the wire.useMessages(stream, subagent)), and released on unmount.apiUrl, client, or fetch alongside a custom transport: AgentServerAdapter is a compile-time error.threadId. Pass threadId: () => active to drive in-place thread swaps; everything else is captured at mount.@langchain/svelte to ^1.0.0 and
@langchain/langgraph-sdk to the matching new event-based streaming
runtime.useStream only — useStreamExperimental is not exported from this package.onFinish, onUpdateEvent, onCustomEvent, onMetadataEvent, onLangChainEvent, onDebugEvent, onCheckpointEvent, onTaskEvent, onStop, onError, fetchStateHistory, throttle, filterSubagentMessages, subagentToolNames.transport: new FetchStreamTransport(…) with transport: new HttpAgentServerAdapter(…) from @langchain/svelte (or @langchain/langgraph-sdk). The adapter is bound to a concrete threadId.branch, setBranch, history, experimental_branchTree, getMessagesMetadata, joinStream, switchThread, queue, activeSubagents, getSubagent, getSubagentsByType, getSubagentsByMessage.useMessageMetadata(stream, () => msg.id) instead of stream.getMessagesMetadata(msg).useSubmissionQueue(stream) instead of stream.queue.threadId: () => active instead of calling stream.switchThread(id).useMessages(stream, subagent) / useToolCalls(stream, subagent) / useValues(stream, subagent) instead of subagent.messages / subagent.toolCalls.onDisconnect and streamResumable are gone from submit(). Use stream.stop() (cancel, default) or stream.disconnect() (join/rejoin) instead (see recipe below).svelte-check / tsc. The option bag and return shape are now strongly discriminated, so most remaining issues surface as type errors mapped to the tables below.| v0 option | v1 equivalent |
|---|---|
assistantId |
same — required on the LGP branch, defaults to "_" for custom adapters |
apiUrl / apiKey |
same |
client |
same |
threadId (static) |
same; also accepts a getter () => string \| null for reactive swapping |
initialValues |
same |
messagesKey |
same (default "messages") |
onThreadId |
same |
onCreated |
fires with { runId }; read the current thread from stream.threadId when needed |
onCompleted |
convenience callback with { runId?, reason } when active streaming ends |
transport |
now "sse" | "websocket" | an AgentServerAdapter instance. HttpAgentServerAdapter requires apiUrl and threadId. |
fetch / webSocketFactory |
same on the LGP branch only |
tools + onTool |
new headless-tool channel (see Headless tools) |
onFinish, onError, onStop |
hook-level callbacks removed — observe stream.isLoading / stream.error reactively, or pass per-submit onError to submit(). Use stream.stop() / stream.disconnect() instead of onStop (see recipe below). |
onDisconnect, streamResumable (on submit) |
removed from submit — use stream.stop() (cancel, default) or stream.disconnect() (join/rejoin) on the stream handle |
onUpdateEvent, onCustomEvent, onDebugEvent, onCheckpointEvent, onTaskEvent, onMetadataEvent, onLangChainEvent |
removed — use useChannel for raw events or the dedicated selector composables |
fetchStateHistory |
removed — fork / branch flows are driven by useMessageMetadata |
throttle |
removed — the controller batches its own notifications |
filterSubagentMessages, subagentToolNames |
removed — subagent views are per-namespace via useMessages(stream, sub) |
| v0 field / method | v1 equivalent |
|---|---|
values, messages, isLoading, error, interrupt, interrupts |
same |
threadId |
same |
isThreadLoading |
same — plus hydrationPromise for SvelteKit load() handlers |
submit(values, options?) |
same — returns Promise<void>; options now include forkFrom, per-submit threadId, per-submit onError, and multitaskStrategy |
stop(options?) |
cancels the active run server-side by default ({ cancel: true }); pass { cancel: false } or call disconnect() for join/rejoin |
disconnect() |
new — alias for stop({ cancel: false }); agent keeps running server-side |
client, assistantId |
same |
toolCalls |
same — assembled tool-call rows at the root |
subagents, subgraphs, subgraphsByNode |
new — discovery snapshots (namespaces). Use them as the target argument to selector composables |
respond(response, options?) / respondAll(responsesById, options?) |
new — resume the agent after an interrupt (respondAll resumes several at the same checkpoint) |
getThread() |
new — returns the bound ThreadStream for low-level protocol access |
branch, setBranch |
removed — there is no global branch pointer with new event-based streaming. Fork flows use submit(input, { forkFrom }) and parentCheckpointId from useMessageMetadata. |
history, experimental_branchTree |
removed — pull history via the Client directly when needed |
getMessagesMetadata(msg) |
useMessageMetadata(stream, () => msg.id) → { parentCheckpointId } |
joinStream(...) |
removed — useStream attaches automatically on mount |
switchThread(id) |
pass threadId: () => active as a getter; update the underlying state |
queue.* |
useSubmissionQueue(stream) → { entries, size, cancel, clear } |
activeSubagents, getSubagent, getSubagentsByType, getSubagentsByMessage |
drop — read through stream.subagents (ReadonlyMap) plus selector composables |
getMessagesMetadata<!-- v0 -->
<script lang="ts">
const meta = stream.getMessagesMetadata(msg, i);
const parent = meta?.firstSeenState?.parent_checkpoint;
</script>
<!-- v1 -->
<script lang="ts">
import { useMessageMetadata } from "@langchain/svelte";
const meta = useMessageMetadata(stream, () => msg.id);
</script>
Parent: {meta.current?.parentCheckpointId ?? "root"}
stream.queue<script lang="ts">
import { useSubmissionQueue } from "@langchain/svelte";
const queue = useSubmissionQueue(stream);
</script>
{#if queue.size > 0}
<p>{queue.size} run(s) pending</p>
<button onclick={() => queue.clear()}>Clear</button>
{/if}
stream.switchThread(id)<script lang="ts">
let active = $state<string | null>(null);
const stream = useStream({
assistantId: "agent",
apiUrl: "http://localhost:2024",
threadId: () => active,
});
</script>
<button onclick={() => (active = crypto.randomUUID())}>New thread</button>
Passing null clears the thread; the next submit() creates a fresh one.
onDisconnect / join-rejoin stop<!-- v0 — disconnect policy on submit -->
<script lang="ts">
await stream.submit(input, { onDisconnect: "continue", streamResumable: true });
// stream.stop() only aborted the client
</script>
<!-- v1 — explicit stop vs disconnect -->
<script lang="ts">
await stream.submit(input);
await stream.stop(); // chat cancel (server + client)
await stream.disconnect(); // join/rejoin (client only)
</script>
runs.cancel is issued only once onCreated has provided a runId.
<script lang="ts">
import { HttpAgentServerAdapter, useStream } from "@langchain/svelte";
const stream = useStream({
transport: new HttpAgentServerAdapter({
apiUrl: "/api/agent",
threadId: "thread-123",
defaultHeaders: { Authorization: `Bearer ${token}` },
}),
});
</script>
When transport is an AgentServerAdapter, LGP-specific options like
client, apiUrl, apiKey, fetch, and webSocketFactory are not
accepted on the useStream option bag; the adapter owns those details.
assistantId remains optional for adapters that multiplex internally.
"rollback" remains the default. "reject" errors client-side when a
run is already in flight, and "enqueue" records the submission in the
client queue exposed by useSubmissionQueue(stream). "interrupt" is
accepted by the type surface and currently falls back to "rollback"
until server-side interrupt semantics land.
<!-- v0 -->
{#each stream.activeSubagents as sub (sub.id)}
{#each sub.messages as msg (msg.id)}
<div>{msg.content}</div>
{/each}
{/each}
<!-- v1 -->
<script lang="ts">
import { useMessages } from "@langchain/svelte";
</script>
{#each [...stream.subagents.values()] as sub (sub.namespace.join("/"))}
{@const subMessages = useMessages(stream, sub)}
{#each subMessages.current as msg (msg.id)}
<div>{msg.content}</div>
{/each}
{/each}
Each mounted useMessages(stream, sub) opens a ref-counted namespace subscription on demand and releases it on unmount.
provideStream / getStreamShare one useStream handle across a subtree with Svelte context:
<!-- ChatContainer.svelte -->
<script lang="ts">
import { provideStream } from "@langchain/svelte";
provideStream({
assistantId: "agent",
apiUrl: "http://localhost:2024",
});
</script>
<ChatHeader />
<MessageList />
<!-- MessageList.svelte -->
<script lang="ts">
import { getStream, useMessages } from "@langchain/svelte";
const stream = getStream();
const messages = useMessages(stream);
</script>
{#each messages.current as msg (msg.id)}
<div>{msg.content}</div>
{/each}
Call provideStream during component initialisation (top-level
<script>). Descendants read the shared handle with getStream().
| Helper | Use |
|---|---|
UseStreamReturn<T> |
Fully-resolved return type of useStream<T>. |
AnyStream |
Type-erased handle (UseStreamReturn<any, any, any>) for components that only forward the stream to selector composables. |
UseStreamOptions |
Discriminated options union (LGP branch vs custom AgentServerAdapter); rarely needed at call sites. |
InferStateType<T> |
Unwraps a compiled graph / agent brand into its state shape. |
UseSubmissionQueueReturn |
Return shape of useSubmissionQueue(stream) (entries, size, cancel, clear). |