diff --git a/docs/docs/cloud/how-tos/use_stream_react.md b/docs/docs/cloud/how-tos/use_stream_react.md
index 0468b9bb9..ee62fcaff 100644
--- a/docs/docs/cloud/how-tos/use_stream_react.md
+++ b/docs/docs/cloud/how-tos/use_stream_react.md
@@ -1,8 +1,6 @@
# How to integrate LangGraph into your React application
-!!! info "Prerequisites"
- - [LangGraph Platform](../../concepts/langgraph_platform.md)
- - [LangGraph Server](../../concepts/langgraph_server.md)
+!!! info "Prerequisites" - [LangGraph Platform](../../concepts/langgraph_platform.md) - [LangGraph Server](../../concepts/langgraph_server.md)
The `useStream()` React hook provides a seamless way to integrate LangGraph into your React applications. It handles all the complexities of streaming, state management, and branching logic, letting you focus on building great chat experiences.
@@ -169,10 +167,7 @@ The `useStream()` hook exposes the `interrupt` property, which will be filled wi
Learn more about interrupts in the [How to handle interrupts](../../how-tos/human_in_the_loop/wait-user-input.ipynb) guide.
```tsx
-const thread = useStream<
- { messages: Message[] },
- { InterruptType: string }
->({
+const thread = useStream<{ messages: Message[] }, { InterruptType: string }>({
apiUrl: "http://localhost:2024",
assistantId: "agent",
messagesKey: "messages",
@@ -182,7 +177,6 @@ if (thread.interrupt) {
return (
Interrupted! {thread.interrupt.value}
-
{
@@ -313,7 +307,7 @@ export default function App() {
onEdit={(message) =>
thread.submit(
{ messages: [message] },
- { checkpoint: parentCheckpoint },
+ { checkpoint: parentCheckpoint }
)
}
/>
@@ -370,6 +364,33 @@ export default function App() {
For advanced use cases you can use the `experimental_branchTree` property to get the tree representation of the thread, which can be used to render branching controls for non-message based graphs.
+### Optimistic Updates
+
+You can optimistically update the client state before performing a network request to the agent, allowing you to provide immediate feedback to the user, such as showing the user message immediately before the agent has seen the request.
+
+```tsx
+const stream = useStream({
+ apiUrl: "http://localhost:2024",
+ assistantId: "agent",
+ messagesKey: "messages",
+});
+
+const handleSubmit = (text: string) => {
+ const newMessage = { type: "human" as const, content: text };
+
+ stream.submit(
+ { messages: [newMessage] },
+ {
+ optimisticValues(prev) {
+ const prevMessages = prev.messages ?? [];
+ const newMessages = [...prevMessages, newMessage];
+ return { ...prev, messages: newMessages };
+ },
+ }
+ );
+};
+```
+
### TypeScript
The `useStream()` hook is friendly for apps written in TypeScript and you can specify types for the state to get better type safety and IDE support.
@@ -397,21 +418,23 @@ You can also optionally specify types for different scenarios, such as:
- `UpdateType`: Type for the submit function (default: `Partial`)
```tsx
-
-const thread = useStream;
- };
- InterruptType: string;
- CustomEventType: {
- type: "progress" | "debug";
- payload: unknown;
- };
- ConfigurableType: {
- model: string;
- };
-}>({
+const thread = useStream<
+ State,
+ {
+ UpdateType: {
+ messages: Message[] | Message;
+ context?: Record;
+ };
+ InterruptType: string;
+ CustomEventType: {
+ type: "progress" | "debug";
+ payload: unknown;
+ };
+ ConfigurableType: {
+ model: string;
+ };
+ }
+>({
apiUrl: "http://localhost:2024",
assistantId: "agent",
messagesKey: "messages",
diff --git a/libs/sdk-js/package.json b/libs/sdk-js/package.json
index f06349dba..3be321092 100644
--- a/libs/sdk-js/package.json
+++ b/libs/sdk-js/package.json
@@ -1,6 +1,6 @@
{
"name": "@langchain/langgraph-sdk",
- "version": "0.0.61",
+ "version": "0.0.62",
"description": "Client library for interacting with the LangGraph API",
"type": "module",
"packageManager": "yarn@1.22.19",
diff --git a/libs/sdk-js/src/react/stream.tsx b/libs/sdk-js/src/react/stream.tsx
index 4586aa3c4..b7e574cff 100644
--- a/libs/sdk-js/src/react/stream.tsx
+++ b/libs/sdk-js/src/react/stream.tsx
@@ -782,6 +782,30 @@ export function useStream<
submittingRef.current = true;
abortRef.current = new AbortController();
+ // Unbranch things
+ const newPath = submitOptions?.checkpoint?.checkpoint_id
+ ? branchByCheckpoint[submitOptions?.checkpoint?.checkpoint_id]?.branch
+ : undefined;
+
+ if (newPath != null) setBranch(newPath ?? "");
+
+ // Assumption: we're setting the initial value
+ // Used for instant feedback
+ setStreamValues(() => {
+ const values = { ...historyValues };
+
+ if (submitOptions?.optimisticValues != null) {
+ return {
+ ...values,
+ ...(typeof submitOptions.optimisticValues === "function"
+ ? submitOptions.optimisticValues(values)
+ : submitOptions.optimisticValues),
+ };
+ }
+
+ return values;
+ });
+
let usableThreadId = threadId;
if (!usableThreadId) {
const thread = await client.threads.create();
@@ -818,30 +842,6 @@ export function useStream<
streamMode,
}) as AsyncGenerator;
- // Unbranch things
- const newPath = submitOptions?.checkpoint?.checkpoint_id
- ? branchByCheckpoint[submitOptions?.checkpoint?.checkpoint_id]?.branch
- : undefined;
-
- if (newPath != null) setBranch(newPath ?? "");
-
- // Assumption: we're setting the initial value
- // Used for instant feedback
- setStreamValues(() => {
- const values = { ...historyValues };
-
- if (submitOptions?.optimisticValues != null) {
- return {
- ...values,
- ...(typeof submitOptions.optimisticValues === "function"
- ? submitOptions.optimisticValues(values)
- : submitOptions.optimisticValues),
- };
- }
-
- return values;
- });
-
let streamError: StreamError | undefined;
for await (const { event, data } of run) {
if (event === "error") {