Skip to content

Commit ae0dc0a

Browse files
authored
feat (ui/react): add resume flag to useChat (vercel#6770)
## Background Resuming streams on load currently requires `useEffect` and `experimental_resume`. The DX can be simplified by offering a `resume` flag on `useChat` that does the same. ## Summary Add `resume` flag to `useChat` (React).
1 parent 3ee56da commit ae0dc0a

File tree

4 files changed

+23
-28
lines changed

4 files changed

+23
-28
lines changed

.changeset/strong-windows-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ai-sdk/react': patch
3+
---
4+
5+
feat (ui/react): add resume flag to useChat

examples/next-openai/app/use-chat-resume/chat.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { useChat } from '@ai-sdk/react';
44
import { DefaultChatTransport, type UIMessage } from 'ai';
55
import Link from 'next/link';
6-
import { useEffect } from 'react';
76
import ChatInput from '@component/chat-input';
87

98
export function Chat({
@@ -15,31 +14,16 @@ export function Chat({
1514
autoResume: boolean;
1615
initialMessages: UIMessage[];
1716
}) {
18-
const {
19-
error,
20-
status,
21-
sendMessage,
22-
messages,
23-
reload,
24-
stop,
25-
experimental_resume,
26-
} = useChat({
17+
const { error, status, sendMessage, messages, reload, stop } = useChat({
2718
id,
2819
messages: initialMessages,
2920
transport: new DefaultChatTransport({ api: '/api/use-chat-resume' }),
3021
onError: error => {
3122
console.error('Error streaming text:', error);
3223
},
24+
resume: autoResume,
3325
});
3426

35-
useEffect(() => {
36-
if (autoResume) {
37-
experimental_resume();
38-
}
39-
// We want to disable the exhaustive deps rule here because we only want to run this effect once
40-
// eslint-disable-next-line react-hooks/exhaustive-deps
41-
}, []);
42-
4327
return (
4428
<div className="flex flex-col w-full max-w-md gap-8 py-24 mx-auto stretch">
4529
<Link href={`/use-chat-resume/${id}`} target="_noblank">

packages/react/src/use-chat.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
type CreateUIMessage,
55
type UIMessage,
66
} from 'ai';
7-
import { useCallback, useRef, useSyncExternalStore } from 'react';
7+
import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';
88
import { Chat } from './chat.react';
99

1010
export type { CreateUIMessage, UIMessage };
@@ -45,10 +45,16 @@ Custom throttle wait in ms for the chat messages and data updates.
4545
Default is undefined, which disables throttling.
4646
*/
4747
experimental_throttle?: number;
48+
49+
/**
50+
* Whether to resume an ongoing chat generation stream.
51+
*/
52+
resume?: boolean;
4853
};
4954

5055
export function useChat<UI_MESSAGE extends UIMessage = UIMessage>({
5156
experimental_throttle: throttleWaitMs,
57+
resume = false,
5258
...options
5359
}: UseChatOptions<UI_MESSAGE> = {}): UseChatHelpers<UI_MESSAGE> {
5460
const chatRef = useRef('chat' in options ? options.chat : new Chat(options));
@@ -87,9 +93,15 @@ export function useChat<UI_MESSAGE extends UIMessage = UIMessage>({
8793

8894
chatRef.current.messages = messagesParam;
8995
},
90-
[messages],
96+
[messages, chatRef],
9197
);
9298

99+
useEffect(() => {
100+
if (resume) {
101+
chatRef.current.experimental_resume();
102+
}
103+
}, [resume, chatRef]);
104+
93105
return {
94106
id: chatRef.current.id,
95107
messages,

packages/react/src/use-chat.ui.test.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,7 @@ describe('resume ongoing stream and return assistant message', () => {
19941994

19951995
setupTestComponent(
19961996
() => {
1997-
const { messages, status, experimental_resume } = useChat({
1997+
const { messages, status } = useChat({
19981998
id: '123',
19991999
messages: [
20002000
{
@@ -2004,15 +2004,9 @@ describe('resume ongoing stream and return assistant message', () => {
20042004
},
20052005
],
20062006
generateId: mockId(),
2007+
resume: true,
20072008
});
20082009

2009-
useEffect(() => {
2010-
experimental_resume();
2011-
2012-
// We want to disable the exhaustive deps rule here because we only want to run this effect once
2013-
// eslint-disable-next-line react-hooks/exhaustive-deps
2014-
}, []);
2015-
20162010
return (
20172011
<div>
20182012
{messages.map((m, idx) => (

0 commit comments

Comments
 (0)