Skip to main content

Documentation Index

Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-cbuipl-1779916257-33d1bcf.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Join and rejoin lets you disconnect from a running agent stream without stopping the agent, then reconnect to it later. The agent continues executing server-side while the client is away, and you pick up the stream exactly where you left off.
This feature requires the LangGraph Agent Server. Run your agent locally with langgraph dev or deploy it to LangSmith to use this pattern.

Why join & rejoin?

Traditional streaming APIs tightly couple the client and server: if the client disconnects, the stream is lost. Join and rejoin breaks this coupling, enabling several important patterns:
  • Network interruptions: mobile users moving between cell towers or Wi-Fi networks can seamlessly resume
  • Page navigation: users navigating away from a chat page and returning later without losing progress
  • Mobile backgrounding: apps suspended by the OS can rejoin the stream when foregrounded
  • Long-running tasks: agents performing multi-minute operations (research, code generation, data analysis) where users don’t need to keep the page open
  • Multi-device handoff: start a conversation on your phone, rejoin on your desktop

Core concepts

The join/rejoin pattern involves three key mechanisms:
Method / OptionPurpose
threadIdBind the stream to the LangGraph thread you want to observe
onThreadIdPersist newly-created thread IDs so a remount can reconnect
stream.disconnect()Leave the stream client-side while the agent keeps running server-side
Remount with the same threadIdReattach to in-flight work for that thread
Join/rejoin uses stream.disconnect(), not stream.stop(). By default, stream.stop() cancels the active run: it disconnects the client and cancels the run on the server. For join/rejoin, call stream.disconnect() (alias for stop({ cancel: false })) so the agent continues processing while you are away.To cancel execution explicitly from app code, use stream.stop() or client.runs.cancel.

Setting up useStream

The key setup step is persisting threadId. When the component remounts with the same thread ID, the stream attaches to the thread’s current state and any in-flight run.
The code examples use useStream<typeof myAgent> for type-safe stream state. See Type inference for Python or JavaScript backends.
import { useStream } from "@langchain/react";
import { useCallback, useState } from "react";

function Chat() {
  const [connected, setConnected] = useState(true);
  const [mountKey, setMountKey] = useState(0);
  const [threadId, setThreadId] = useState<string | null>(
    () => sessionStorage.getItem("activeThreadId"),
  );

  const stream = useStream<typeof myAgent>({
    apiUrl: "http://localhost:2024",
    assistantId: "join_rejoin",
    threadId,
    onThreadId(id) {
      setThreadId(id);
      if (id) sessionStorage.setItem("activeThreadId", id);
    },
  });

  const disconnect = useCallback(() => {
    void stream.disconnect();
    setConnected(false);
  }, [stream]);

  const rejoin = useCallback(() => {
    setMountKey((key) => key + 1);
    setConnected(true);
  }, []);

  return (
    <div key={mountKey}>
      <ConnectionStatus connected={connected} />
      <MessageList messages={stream.messages} />
      <ChatControls
        stream={stream}
        threadId={threadId}
        connected={connected}
        onDisconnect={disconnect}
        onRejoin={rejoin}
      />
    </div>
  );
}

Submitting messages

Submit messages normally. The thread ID binding is what allows a later remount to reconnect to the same conversation:
stream.submit({ messages: [{ type: "human", content: text }] });

Disconnecting from a stream

Call stream.disconnect() to leave the stream without cancelling the run. The agent continues processing server-side.
await stream.disconnect();
// equivalent to: await stream.stop({ cancel: false })
Do not use stream.stop() here — by default it cancels the run on the server. After calling disconnect():
  • stream.isLoading becomes false
  • Your own connected flag should also become false
  • The message list retains all messages received up to the disconnect point
  • The agent continues running on the server
  • No new messages are received until you rejoin

Rejoining a stream

Remount the stream consumer with the saved thread ID to reconnect. In React, the demo bumps a mountKey; in other frameworks, use the equivalent remount or conditional-render pattern:
setMountKey((key) => key + 1);
setConnected(true);
After rejoining:
  • connected becomes true
  • Any messages generated while disconnected are delivered
  • New streaming messages resume in real-time
  • If the agent is still running, stream.isLoading becomes true; if it has already finished, you receive the final state immediately

Best practices

  • Use disconnect() for join/rejoin, stop() to cancel: navigating away or backgrounding the app should call stream.disconnect(). A user-facing “Stop” or “Cancel” button should call stream.stop() (or client.runs.cancel).
  • Always save the thread ID: without it, rejoining is impossible. Use both component state and persistent storage for resilience.
  • Show clear connection state: users should always know whether they are receiving live updates or viewing a snapshot.
  • Auto-rejoin on visibility change: use the Page Visibility API to automatically rejoin when the user returns to the tab.
  • Set reasonable timeouts: if a rejoin attempt takes too long, fall back to fetching the thread history instead.
  • Clean up stale threads: remove persisted thread IDs when the user starts over or the backend reports that the thread is unavailable.